Repository: GaijinEntertainment/quirrel Branch: master Commit: dec4877203e5 Files: 1558 Total size: 3.6 MB Directory structure: gitextract_7iu7af3r/ ├── .github/ │ └── workflows/ │ └── docs_build_deploy.yaml ├── .gitignore ├── .travis.yml ├── CMakeLists.txt ├── COPYRIGHT ├── HISTORY ├── README.md ├── appveyor.yml ├── doc/ │ ├── .gitignore │ ├── Makefile │ ├── make.bat │ ├── repl/ │ │ ├── .gitignore │ │ ├── CMakeLists.txt │ │ ├── build.txt │ │ ├── native.cpp │ │ └── static/ │ │ ├── repl-rst.css │ │ ├── repl.css │ │ ├── repl.html │ │ └── replmain.js │ ├── requirements.txt │ └── source/ │ ├── _static/ │ │ └── custom.css │ ├── _templates/ │ │ └── page.html │ ├── conf.py │ ├── diff_from_squirrel.rst │ ├── index.rst │ ├── introduction.rst │ ├── modules/ │ │ ├── bindings.rst │ │ └── index.rst │ ├── quirrel.py │ ├── quirrel_pygment_lexer.py │ ├── reference/ │ │ ├── api/ │ │ │ ├── bytecode_serialization.rst │ │ │ ├── calls.rst │ │ │ ├── compiler.rst │ │ │ ├── debug_interface.rst │ │ │ ├── garbage_collector.rst │ │ │ ├── object_creation_and_handling.rst │ │ │ ├── object_manipulation.rst │ │ │ ├── raw_object_handling.rst │ │ │ ├── stack_operations.rst │ │ │ └── virtual_machine.rst │ │ ├── api_reference.rst │ │ ├── embedding/ │ │ │ ├── build_configuration.rst │ │ │ ├── calling_a_function.rst │ │ │ ├── compiling_a_script.rst │ │ │ ├── creating_a_c_function.rst │ │ │ ├── debug_interface.rst │ │ │ ├── error_conventions.rst │ │ │ ├── memory_management.rst │ │ │ ├── references_from_c.rst │ │ │ ├── runtime_error_handling.rst │ │ │ ├── tables_and_arrays_manipulation.rst │ │ │ ├── the_registry_table.rst │ │ │ ├── the_stack.rst │ │ │ ├── userdata_and_userpointers.rst │ │ │ └── vm_initialization.rst │ │ ├── embedding_squirrel.rst │ │ ├── index.rst │ │ ├── language/ │ │ │ ├── arrays.rst │ │ │ ├── builtin_functions.rst │ │ │ ├── classes.rst │ │ │ ├── compiler_directives.rst │ │ │ ├── constants_and_enumerations.rst │ │ │ ├── datatypes.rst │ │ │ ├── destructuring_assignment.rst │ │ │ ├── expressions.rst │ │ │ ├── functions.rst │ │ │ ├── generators.rst │ │ │ ├── lexical_structure.rst │ │ │ ├── limitations.rst │ │ │ ├── metamethods.rst │ │ │ ├── statements.rst │ │ │ ├── string_interpolation.rst │ │ │ ├── tables.rst │ │ │ ├── threads.rst │ │ │ ├── type_annotations.rst │ │ │ └── weak_references.rst │ │ └── language.rst │ ├── repl/ │ │ └── index.rst │ ├── rfcs/ │ │ ├── README.md │ │ └── STATUS.md │ └── stdlib/ │ ├── index.rst │ ├── introduction.rst │ ├── stdauxlib.rst │ ├── stddatetimelib.rst │ ├── stddebuglib.rst │ ├── stdiolib.rst │ ├── stdiostreamlib.rst │ ├── stdmathlib.rst │ ├── stdstringlib.rst │ └── stdsystemlib.rst ├── helpers/ │ └── keyValueFile.h ├── include/ │ ├── sq_char_class.h │ ├── sqconfig.h │ ├── sqext.h │ ├── sqio.h │ ├── sqstdaux.h │ ├── sqstdblob.h │ ├── sqstddatetime.h │ ├── sqstddebug.h │ ├── sqstdio.h │ ├── sqstdmath.h │ ├── sqstdstring.h │ ├── sqstdsystem.h │ └── squirrel.h ├── internal/ │ ├── sq_safe_shift.h │ └── sqstringlib.h ├── scripts/ │ ├── samples/ │ │ ├── ackermann.nut │ │ ├── array.nut │ │ ├── class.nut │ │ ├── fibonacci.nut │ │ ├── flow.nut │ │ ├── generators.nut │ │ ├── hello.nut │ │ ├── list.nut │ │ ├── loops.nut │ │ ├── matrix.nut │ │ ├── metamethods.nut │ │ ├── methcall.nut │ │ ├── module_1.nut │ │ ├── module_2.nut │ │ ├── module_demo.nut │ │ ├── regex.nut │ │ └── tailstate.nut │ └── std/ │ ├── analyzer.nut │ └── functools.nut ├── sq/ │ ├── CMakeLists.txt │ ├── sq.cpp │ └── sq_test_natives.cpp ├── sqmodules/ │ ├── CMakeLists.txt │ ├── deffileaccess.cpp │ ├── helpers.cpp │ ├── helpers.h │ ├── path.cpp │ ├── path.h │ ├── span.h │ ├── sqmodules.cpp │ └── sqmodules.h ├── sqrat/ │ ├── README.txt │ └── include/ │ ├── sqrat/ │ │ ├── sqratAllocator.h │ │ ├── sqratArray.h │ │ ├── sqratClass.h │ │ ├── sqratClassType.h │ │ ├── sqratConst.h │ │ ├── sqratFunction.h │ │ ├── sqratGlobalMethods.h │ │ ├── sqratMemberMethods.h │ │ ├── sqratObject.h │ │ ├── sqratScript.h │ │ ├── sqratTable.h │ │ ├── sqratTypes.h │ │ └── sqratUtil.h │ └── sqrat.h ├── sqstdlib/ │ ├── CMakeLists.txt │ ├── sqstdaux.cpp │ ├── sqstdblob.cpp │ ├── sqstdblobimpl.h │ ├── sqstddatetime.cpp │ ├── sqstddebug.cpp │ ├── sqstdhash.cpp │ ├── sqstdhash.h │ ├── sqstdio.cpp │ ├── sqstdmath.cpp │ ├── sqstdrex.cpp │ ├── sqstdserialization.cpp │ ├── sqstdserialization.h │ ├── sqstdstream.cpp │ ├── sqstdstream.h │ ├── sqstdstring.cpp │ └── sqstdsystem.cpp ├── squirrel/ │ ├── CMakeLists.txt │ ├── ast_tools/ │ │ └── ast_indent_render.h │ ├── compiler/ │ │ ├── CMakeLists.txt │ │ ├── COPYRIGHT │ │ ├── arena.h │ │ ├── ast.cpp │ │ ├── ast.h │ │ ├── codegen.cpp │ │ ├── codegen.h │ │ ├── compilationcontext.cpp │ │ ├── compilationcontext.h │ │ ├── compiler.cpp │ │ ├── compiler.h │ │ ├── constgen.cpp │ │ ├── constgen.h │ │ ├── lex_tokens.h │ │ ├── lexer.cpp │ │ ├── lexer.h │ │ ├── optimizations/ │ │ │ ├── closureHoisting.cpp │ │ │ └── closureHoisting.h │ │ ├── optimizer.cpp │ │ ├── optimizer.h │ │ ├── parser.cpp │ │ ├── parser.h │ │ ├── sourceloc.h │ │ ├── sqdump.cpp │ │ ├── sqfuncstate.cpp │ │ ├── sqfuncstate.h │ │ ├── sqio.cpp │ │ ├── sqtypeparser.cpp │ │ ├── sqtypeparser.h │ │ ├── static_analyzer/ │ │ │ ├── analyzer.cpp │ │ │ ├── analyzer.h │ │ │ ├── analyzer_internal.h │ │ │ ├── assign_seq_terminator.h │ │ │ ├── ast_helpers.h │ │ │ ├── breakable_scope.h │ │ │ ├── checker_visitor.cpp │ │ │ ├── checker_visitor.h │ │ │ ├── config.cpp │ │ │ ├── config.h │ │ │ ├── function_info.h │ │ │ ├── function_ret_type_eval.cpp │ │ │ ├── function_ret_type_eval.h │ │ │ ├── global_state.cpp │ │ │ ├── global_state.h │ │ │ ├── loop_terminator_collector.h │ │ │ ├── modification_checker.h │ │ │ ├── name_shadowing_checker.cpp │ │ │ ├── name_shadowing_checker.h │ │ │ ├── naming.cpp │ │ │ ├── naming.h │ │ │ ├── node_complexity_counter.h │ │ │ ├── node_diff_computer.h │ │ │ ├── node_equal_checker.h │ │ │ ├── operator_classification.h │ │ │ ├── symbol_info.h │ │ │ ├── value_ref.cpp │ │ │ ├── value_ref.h │ │ │ ├── var_scope.cpp │ │ │ └── var_scope.h │ │ └── typeinference.cpp │ ├── opcodes.h │ ├── sqapi.cpp │ ├── sqarray.h │ ├── sqbaselib.cpp │ ├── sqclass.cpp │ ├── sqclass.h │ ├── sqclosure.h │ ├── sqdebug.cpp │ ├── sqdedupshrinker.cpp │ ├── sqext.cpp │ ├── sqfuncproto.h │ ├── sqmem.cpp │ ├── sqobject.cpp │ ├── sqobject.h │ ├── sqpcheader.h │ ├── sqstate.cpp │ ├── sqstate.h │ ├── sqstring.h │ ├── sqstringlib.cpp │ ├── sqtable.cpp │ ├── sqtable.h │ ├── squserdata.h │ ├── squtils.h │ ├── sqvm.cpp │ ├── sqvm.h │ ├── vartrace.cpp │ ├── vartrace.h │ └── vartracestub.cpp ├── squirrel-config.cmake.in ├── testData/ │ ├── ast/ │ │ ├── ast_render/ │ │ │ ├── all_syntax.nut │ │ │ └── all_syntax.opt.txt │ │ └── optimizations/ │ │ └── closureHoisting/ │ │ ├── classCrossFunc.nut │ │ ├── classCrossFunc.opt.txt │ │ ├── classSimple.nut │ │ ├── classSimple.opt.txt │ │ ├── constDep.nut │ │ ├── constDep.opt.txt │ │ ├── constFunc.nut │ │ ├── constFunc.opt.txt │ │ ├── deepUnchained.nut │ │ ├── deepUnchained.opt.txt │ │ ├── externalSymbol.nut │ │ ├── externalSymbol.opt.txt │ │ ├── implicitChainedFuncs.nut │ │ ├── implicitChainedFuncs.opt.txt │ │ ├── implicitChainedFuncs2.nut │ │ ├── implicitChainedFuncs2.opt.txt │ │ ├── indirectLocalCapture.nut │ │ ├── indirectLocalCapture.opt.txt │ │ ├── manyFuncs.nut │ │ ├── manyFuncs.opt.txt │ │ ├── multiDepthCapture.nut │ │ ├── multiDepthCapture.opt.txt │ │ ├── nestedParamShadow.nut │ │ ├── nestedParamShadow.opt.txt │ │ ├── paramDefaultCapture.nut │ │ ├── paramDefaultCapture.opt.txt │ │ ├── recursion1.nut │ │ ├── recursion1.opt.txt │ │ ├── simple.nut │ │ ├── simple.opt.txt │ │ ├── simple2.nut │ │ ├── simple2.opt.txt │ │ ├── tooManyLocals.nut │ │ ├── tooManyLocals.opt.txt │ │ ├── typedDefaultReturnType.nut │ │ └── typedDefaultReturnType.opt.txt │ ├── diagnostics/ │ │ ├── 1000_local_variables.diag.txt │ │ ├── 1000_local_variables.nut │ │ ├── 50k_access_member.diag.txt │ │ ├── 50k_access_member.nut │ │ ├── 50k_curly_brackets.diag.txt │ │ ├── 50k_curly_brackets.nut │ │ ├── 50k_function_calls.diag.txt │ │ ├── 50k_function_calls.nut │ │ ├── 50k_lambdas.diag.txt │ │ ├── 50k_lambdas.nut │ │ ├── 50k_nested_tables.diag.txt │ │ ├── 50k_nested_tables.nut │ │ ├── 50k_not.diag.txt │ │ ├── 50k_not.nut │ │ ├── 50k_paren_brackets.diag.txt │ │ ├── 50k_paren_brackets.nut │ │ ├── 50k_square_brackets.diag.txt │ │ ├── 50k_square_brackets.nut │ │ ├── 50r_binop.diag.txt │ │ ├── 50r_binop.nut │ │ ├── assign_to_expr.diag.txt │ │ ├── assign_to_expr.nut │ │ ├── assign_to_expr2.diag.txt │ │ ├── assign_to_expr2.nut │ │ ├── assign_to_optional_7.diag.txt │ │ ├── assign_to_optional_7.nut │ │ ├── base_as_array_assign.diag.txt │ │ ├── base_as_array_assign.nut │ │ ├── base_as_array_pp.diag.txt │ │ ├── base_as_array_pp.nut │ │ ├── base_pp.diag.txt │ │ ├── base_pp.nut │ │ ├── base_x_pp.diag.txt │ │ ├── base_x_pp.nut │ │ ├── binding_assign.diag.txt │ │ ├── binding_assign.nut │ │ ├── clone_op_allowed.diag.txt │ │ ├── clone_op_allowed.nut.txt │ │ ├── clone_op_forbiden.diag.txt │ │ ├── clone_op_forbiden.nut.txt │ │ ├── compilation_errors/ │ │ │ ├── lex_errors/ │ │ │ │ ├── empty_literal.diag.txt │ │ │ │ ├── empty_literal.nut │ │ │ │ ├── fp_exp_expected.diag.txt │ │ │ │ ├── fp_exp_expected.nut │ │ │ │ ├── hex_digits_expected.diag.txt │ │ │ │ ├── hex_digits_expected.nut │ │ │ │ ├── hex_numbers_expected.diag.txt │ │ │ │ ├── hex_numbers_expected.nut │ │ │ │ ├── hex_too_many_digits.diag.txt │ │ │ │ ├── hex_too_many_digits.nut │ │ │ │ ├── invalid_token.diag.txt │ │ │ │ ├── invalid_token.nut │ │ │ │ ├── literal_overflow.diag.txt │ │ │ │ ├── literal_overflow.nut │ │ │ │ ├── literal_underflow.diag.txt │ │ │ │ ├── literal_underflow.nut │ │ │ │ ├── malformed_number.diag.txt │ │ │ │ ├── malformed_number.nut │ │ │ │ ├── newline_in_const.diag.txt │ │ │ │ ├── newline_in_const.nut │ │ │ │ ├── octal_not_supported.diag.txt │ │ │ │ ├── octal_not_supported.nut │ │ │ │ ├── trailing_block_comment.diag.txt │ │ │ │ ├── trailing_block_comment.nut │ │ │ │ ├── unexpected_char.diag.txt │ │ │ │ ├── unexpected_char.nut │ │ │ │ ├── unfinished_string.diag.txt │ │ │ │ ├── unfinished_string.nut │ │ │ │ ├── unrecognised_escape.diag.txt │ │ │ │ └── unrecognised_escape.nut │ │ │ ├── sema_errors/ │ │ │ │ ├── assign_to_binding.diag.txt │ │ │ │ ├── assign_to_binding.nut │ │ │ │ ├── assign_to_expr.diag.txt │ │ │ │ ├── assign_to_expr.nut │ │ │ │ ├── cannot_delete.diag.txt │ │ │ │ ├── cannot_delete.nut │ │ │ │ ├── conflicts_with.diag.txt │ │ │ │ ├── conflicts_with.nut │ │ │ │ ├── constant_field_not_found.diag.txt │ │ │ │ ├── constant_field_not_found.nut │ │ │ │ ├── constant_slot_not_found.diag.txt │ │ │ │ ├── constant_slot_not_found.nut │ │ │ │ ├── duplicate_func_attr.diag.txt │ │ │ │ ├── duplicate_func_attr.nut │ │ │ │ ├── duplicate_key.diag.txt │ │ │ │ ├── duplicate_key.nut │ │ │ │ ├── id_is_not_const.diag.txt │ │ │ │ ├── id_is_not_const.nut │ │ │ │ ├── inc_dec_not_assignable.diag.txt │ │ │ │ ├── inc_dec_not_assignable.nut │ │ │ │ ├── initialization_required.diag.txt │ │ │ │ ├── initialization_required.nut │ │ │ │ ├── invalid_enum.diag.txt │ │ │ │ ├── invalid_enum.nut │ │ │ │ ├── local_slot_create.diag.txt │ │ │ │ ├── local_slot_create.nut │ │ │ │ ├── loop_controller_not_in_loop_break.diag.txt │ │ │ │ ├── loop_controller_not_in_loop_break.nut │ │ │ │ ├── loop_controller_not_in_loop_continue.diag.txt │ │ │ │ ├── loop_controller_not_in_loop_continue.nut │ │ │ │ ├── not_allowed_in_const.diag.txt │ │ │ │ ├── not_allowed_in_const.nut │ │ │ │ ├── only_single_variable_declaration.diag.txt │ │ │ │ ├── only_single_variable_declaration.nut │ │ │ │ ├── same_foreach_kv_names.diag.txt │ │ │ │ ├── same_foreach_kv_names.nut │ │ │ │ ├── space_sep_field_name.diag.txt │ │ │ │ ├── space_sep_field_name.nut │ │ │ │ ├── too_many_locals.diag.txt │ │ │ │ ├── too_many_locals.nut │ │ │ │ ├── type_differs.diag.txt │ │ │ │ ├── type_differs.nut │ │ │ │ ├── uninitialized_binding.diag.txt │ │ │ │ ├── uninitialized_binding.nut │ │ │ │ ├── unknown_symbol.diag.txt │ │ │ │ └── unknown_symbol.nut │ │ │ ├── syntax_errors/ │ │ │ │ ├── assign_inside_forbidden.diag.txt │ │ │ │ ├── assign_inside_forbidden.nut │ │ │ │ ├── broken_slot_declaration.diag.txt │ │ │ │ ├── broken_slot_declaration.nut │ │ │ │ ├── compiler_internals_forbidden.diag.txt │ │ │ │ ├── compiler_internals_forbidden.nut │ │ │ │ ├── delete_op_forbidden.diag.txt │ │ │ │ ├── delete_op_forbidden.nut │ │ │ │ ├── end_of_stmt_expected.diag.txt │ │ │ │ ├── end_of_stmt_expected.nut │ │ │ │ ├── expected_bracket.diag.txt │ │ │ │ ├── expected_bracket.nut │ │ │ │ ├── expected_colnum.diag.txt │ │ │ │ ├── expected_colnum.nut │ │ │ │ ├── expected_expression.diag.txt │ │ │ │ ├── expected_expression.nut │ │ │ │ ├── expected_identifier.diag.txt │ │ │ │ ├── expected_identifier.nut │ │ │ │ ├── expected_linenum.diag.txt │ │ │ │ ├── expected_linenum.nut │ │ │ │ ├── expected_token_brace.diag.txt │ │ │ │ ├── expected_token_brace.nut │ │ │ │ ├── expected_token_paren.diag.txt │ │ │ │ ├── expected_token_paren.nut │ │ │ │ ├── expected_while.diag.txt │ │ │ │ ├── expected_while.nut │ │ │ │ ├── global_consts_only.diag.txt │ │ │ │ ├── global_consts_only.nut │ │ │ │ ├── invalid_type_name.diag.txt │ │ │ │ ├── invalid_type_name.nut │ │ │ │ ├── invalid_type_name_suggestion.diag.txt │ │ │ │ ├── invalid_type_name_suggestion.nut │ │ │ │ ├── multiple_docstrings.diag.txt │ │ │ │ ├── multiple_docstrings.nut │ │ │ │ ├── root_table_forbidden.diag.txt │ │ │ │ ├── root_table_forbidden.nut │ │ │ │ ├── scalar_expected.diag.txt │ │ │ │ ├── scalar_expected.nut │ │ │ │ ├── unsupported_directive.diag.txt │ │ │ │ ├── unsupported_directive.nut │ │ │ │ ├── vararg_with_default.diag.txt │ │ │ │ └── vararg_with_default.nut │ │ │ └── type_inference/ │ │ │ ├── test_arithmetic_mismatch.diag.txt │ │ │ ├── test_arithmetic_mismatch.nut │ │ │ ├── test_array_literal.diag.txt │ │ │ ├── test_array_literal.nut │ │ │ ├── test_array_return.diag.txt │ │ │ ├── test_array_return.nut │ │ │ ├── test_array_to_int.diag.txt │ │ │ ├── test_array_to_int.nut │ │ │ ├── test_assignment_chain.diag.txt │ │ │ ├── test_assignment_chain.nut │ │ │ ├── test_call_result_type.diag.txt │ │ │ ├── test_call_result_type.nut │ │ │ ├── test_call_union_return.diag.txt │ │ │ ├── test_call_union_return.nut │ │ │ ├── test_class_literal.diag.txt │ │ │ ├── test_class_literal.nut │ │ │ ├── test_comparison_to_int.diag.txt │ │ │ ├── test_comparison_to_int.nut │ │ │ ├── test_complex_assign_mismatch.diag.txt │ │ │ ├── test_complex_assign_mismatch.nut │ │ │ ├── test_complex_bitwise.diag.txt │ │ │ ├── test_complex_bitwise.nut │ │ │ ├── test_complex_call_chain.diag.txt │ │ │ ├── test_complex_call_chain.nut │ │ │ ├── test_complex_deep_nesting.diag.txt │ │ │ ├── test_complex_deep_nesting.nut │ │ │ ├── test_complex_logical.diag.txt │ │ │ ├── test_complex_logical.nut │ │ │ ├── test_complex_mega.diag.txt │ │ │ ├── test_complex_mega.nut │ │ │ ├── test_complex_mixed_arithmetic.diag.txt │ │ │ ├── test_complex_mixed_arithmetic.nut │ │ │ ├── test_complex_multifunction.diag.txt │ │ │ ├── test_complex_multifunction.nut │ │ │ ├── test_complex_nested_ternary.diag.txt │ │ │ ├── test_complex_nested_ternary.nut │ │ │ ├── test_complex_nullcoalesce.diag.txt │ │ │ ├── test_complex_nullcoalesce.nut │ │ │ ├── test_complex_pure_chain.diag.txt │ │ │ ├── test_complex_pure_chain.nut │ │ │ ├── test_complex_return_expr.diag.txt │ │ │ ├── test_complex_return_expr.nut │ │ │ ├── test_complex_table_ternary.diag.txt │ │ │ ├── test_complex_table_ternary.nut │ │ │ ├── test_const_array.diag.txt │ │ │ ├── test_const_array.nut │ │ │ ├── test_const_array2.diag.txt │ │ │ ├── test_const_array2.nut │ │ │ ├── test_const_array3.diag.txt │ │ │ ├── test_const_array3.nut │ │ │ ├── test_const_array4.diag.txt │ │ │ ├── test_const_array4.nut │ │ │ ├── test_const_function.diag.txt │ │ │ ├── test_const_function.nut │ │ │ ├── test_const_inline.diag.txt │ │ │ ├── test_const_inline.nut │ │ │ ├── test_const_table.diag.txt │ │ │ ├── test_const_table.nut │ │ │ ├── test_destructure_array_default_mismatch.diag.txt │ │ │ ├── test_destructure_array_default_mismatch.nut │ │ │ ├── test_destructure_complex.diag.txt │ │ │ ├── test_destructure_complex.nut │ │ │ ├── test_destructure_table_mismatch.diag.txt │ │ │ ├── test_destructure_table_mismatch.nut │ │ │ ├── test_field_access_unknown.diag.txt │ │ │ ├── test_field_access_unknown.nut │ │ │ ├── test_freeze_type_mismatch.diag.txt │ │ │ ├── test_freeze_type_mismatch.nut │ │ │ ├── test_function_literal.diag.txt │ │ │ ├── test_function_literal.nut │ │ │ ├── test_instance_from_class.diag.txt │ │ │ ├── test_instance_from_class.nut │ │ │ ├── test_literal_float_to_int.diag.txt │ │ │ ├── test_literal_float_to_int.nut │ │ │ ├── test_literal_int_to_string.diag.txt │ │ │ ├── test_literal_int_to_string.nut │ │ │ ├── test_literal_string_to_int.diag.txt │ │ │ ├── test_literal_string_to_int.nut │ │ │ ├── test_return_float_to_int.diag.txt │ │ │ ├── test_return_float_to_int.nut │ │ │ ├── test_return_literal_mismatch.diag.txt │ │ │ ├── test_return_literal_mismatch.nut │ │ │ ├── test_slot_access_unknown.diag.txt │ │ │ ├── test_slot_access_unknown.nut │ │ │ ├── test_string_concat_to_int.diag.txt │ │ │ ├── test_string_concat_to_int.nut │ │ │ ├── test_table_literal.diag.txt │ │ │ ├── test_table_literal.nut │ │ │ ├── test_table_return.diag.txt │ │ │ ├── test_table_return.nut │ │ │ ├── test_table_to_string.diag.txt │ │ │ ├── test_table_to_string.nut │ │ │ ├── test_ternary_mismatch.diag.txt │ │ │ ├── test_ternary_mismatch.nut │ │ │ ├── test_typeof_to_int.diag.txt │ │ │ ├── test_typeof_to_int.nut │ │ │ ├── test_unknown_passthrough.diag.txt │ │ │ └── test_unknown_passthrough.nut │ │ ├── const_scope_1.diag.txt │ │ ├── const_scope_1.nut │ │ ├── const_scope_2.diag.txt │ │ ├── const_scope_2.nut │ │ ├── const_scope_3.diag.txt │ │ ├── const_scope_3.nut │ │ ├── continue_in_codeblock.diag.txt │ │ ├── continue_in_codeblock.nut │ │ ├── delete_base.diag.txt │ │ ├── delete_base.nut │ │ ├── delete_forbid_pragma.diag.txt │ │ ├── delete_forbid_pragma.nut.txt │ │ ├── destrucuring_var_decl_in_if.diag.txt │ │ ├── destrucuring_var_decl_in_if.nut │ │ ├── float_overflow.diag.txt │ │ ├── float_overflow.nut │ │ ├── float_underflow.diag.txt │ │ ├── float_underflow.nut │ │ ├── foreach_destr_typed_default.diag.txt │ │ ├── foreach_destr_typed_default.nut │ │ ├── hex_overflow.diag.txt │ │ ├── hex_overflow.nut │ │ ├── if_var_decl_init.diag.txt │ │ ├── if_var_decl_init.nut │ │ ├── import_01.diag.txt │ │ ├── import_01.nut │ │ ├── import_02.diag.txt │ │ ├── import_02.nut │ │ ├── import_03.diag.txt │ │ ├── import_03.nut │ │ ├── import_04.diag.txt │ │ ├── import_04.nut │ │ ├── import_05.diag.txt │ │ ├── import_05.nut │ │ ├── import_06.diag.txt │ │ ├── import_06.nut │ │ ├── import_07.diag.txt │ │ ├── import_07.nut │ │ ├── import_error.diag.txt │ │ ├── import_error.nut │ │ ├── init_with_wrong_type.diag.txt │ │ ├── init_with_wrong_type.nut │ │ ├── integer_overflow.diag.txt │ │ ├── integer_overflow.nut │ │ ├── integer_overflow_2.diag.txt │ │ ├── integer_overflow_2.nut │ │ ├── integer_overflow_int_max.diag.txt │ │ ├── integer_overflow_int_max.nut │ │ ├── interp_str_hanging_missed_ccurvy.diag.txt │ │ ├── interp_str_hanging_missed_ccurvy.nut │ │ ├── interp_str_not_string.diag.txt │ │ ├── interp_str_not_string.nut │ │ ├── interp_str_not_string_2.diag.txt │ │ ├── interp_str_not_string_2.nut │ │ ├── interp_str_wrong_template.diag.txt │ │ ├── interp_str_wrong_template.nut │ │ ├── invalid_float_1.diag.txt │ │ ├── invalid_float_1.nut │ │ ├── invalid_float_2.diag.txt │ │ ├── invalid_float_2.nut │ │ ├── invalid_float_3.diag.txt │ │ ├── invalid_float_3.nut │ │ ├── invalid_string_interp_1.diag.txt │ │ ├── invalid_string_interp_1.nut │ │ ├── invalid_type_hint_1.diag.txt │ │ ├── invalid_type_hint_1.nut │ │ ├── invalid_type_hint_2.diag.txt │ │ ├── invalid_type_hint_2.nut │ │ ├── invalid_type_hint_3.diag.txt │ │ ├── invalid_type_hint_3.nut │ │ ├── leading_zero_1.diag.txt │ │ ├── leading_zero_1.nut │ │ ├── leading_zero_2.diag.txt │ │ ├── leading_zero_2.nut │ │ ├── leading_zero_3.diag.txt │ │ ├── leading_zero_3.nut │ │ ├── letAssign.diag.txt │ │ ├── letAssign.nut │ │ ├── many_args_in_function_call.diag.txt │ │ ├── many_args_in_function_call.nut │ │ ├── need_space_after_float.diag.txt │ │ ├── need_space_after_float.nut │ │ ├── need_space_after_hex.diag.txt │ │ ├── need_space_after_hex.nut │ │ ├── need_space_after_int.diag.txt │ │ ├── need_space_after_int.nut │ │ ├── return_type_check_1.diag.txt │ │ ├── return_type_check_1.nut │ │ ├── single_var_decl_in_if.diag.txt │ │ ├── single_var_decl_in_if.nut │ │ ├── space_sep_name.diag.txt │ │ ├── space_sep_name.nut.txt │ │ ├── space_sep_name_space.diag.txt │ │ ├── space_sep_name_space.nut.txt │ │ ├── too_large_static_memo_expr.diag.txt │ │ ├── too_large_static_memo_expr.nut.txt │ │ ├── too_many_locals2.diag.txt │ │ ├── too_many_locals2.nut │ │ ├── type_hints_01.diag.txt │ │ ├── type_hints_01.nut │ │ ├── type_hints_02.diag.txt │ │ ├── type_hints_02.nut │ │ ├── type_hints_03.diag.txt │ │ ├── type_hints_03.nut │ │ ├── type_hints_04.diag.txt │ │ ├── type_hints_04.nut │ │ ├── type_hints_05.diag.txt │ │ ├── type_hints_05.nut │ │ ├── type_hints_06.diag.txt │ │ ├── type_hints_06.nut │ │ ├── unfinished_hex.diag.txt │ │ ├── unfinished_hex.nut │ │ ├── var_scope_1.diag.txt │ │ ├── var_scope_1.nut │ │ ├── var_scope_2.diag.txt │ │ ├── var_scope_2.nut │ │ ├── var_scope_3.diag.txt │ │ └── var_scope_3.nut │ ├── exec/ │ │ ├── array_methods.nut │ │ ├── array_methods.out │ │ ├── basics.nut │ │ ├── basics.out │ │ ├── call_constructor_recursion.nut │ │ ├── call_constructor_recursion.out │ │ ├── class_yield.nut │ │ ├── class_yield.out │ │ ├── closure_hoist_typed_default.nut │ │ ├── closure_hoist_typed_default.out │ │ ├── closure_hoist_typed_return.nut │ │ ├── closure_hoist_typed_return.out │ │ ├── compare_int_float.nut │ │ ├── compare_int_float.out │ │ ├── compare_int_int.nut │ │ ├── compare_int_int.out │ │ ├── const_fold.nut │ │ ├── const_fold.out │ │ ├── const_in_closures.nut │ │ ├── const_in_closures.out │ │ ├── coroutines.nut │ │ ├── coroutines.out │ │ ├── deep_loop_variable.nut │ │ ├── deep_loop_variable.out │ │ ├── depth_check.nut │ │ ├── depth_check.out │ │ ├── destructuring/ │ │ │ ├── foreach_capture_destruct_idx.nut │ │ │ ├── foreach_capture_destruct_idx.out │ │ │ ├── foreach_capture_idx_val.nut │ │ │ ├── foreach_capture_idx_val.out │ │ │ ├── foreach_capture_plain_val.nut │ │ │ ├── foreach_capture_plain_val.out │ │ │ ├── foreach_capture_shadowed.nut │ │ │ ├── foreach_capture_shadowed.out │ │ │ ├── foreach_destr_default_closure.nut │ │ │ ├── foreach_destr_default_closure.out │ │ │ ├── foreach_destruct_empty_pattern.nut │ │ │ ├── foreach_destruct_empty_pattern.out │ │ │ ├── foreach_destructuring.nut │ │ │ ├── foreach_destructuring.out │ │ │ ├── foreach_destructuring_branches.nut │ │ │ ├── foreach_destructuring_branches.out │ │ │ ├── foreach_destructuring_complex.nut │ │ │ ├── foreach_destructuring_complex.out │ │ │ ├── foreach_no_capture.nut │ │ │ ├── foreach_no_capture.out │ │ │ ├── function_param_destructuring.nut │ │ │ └── function_param_destructuring.out │ │ ├── div64_by_minus_one_opt.nut │ │ ├── div64_by_minus_one_opt.out │ │ ├── div64_by_minus_one_vm.nut │ │ ├── div64_by_minus_one_vm.out │ │ ├── div_by_minus_one_opt.nut │ │ ├── div_by_minus_one_opt.out │ │ ├── div_by_minus_one_vm.nut │ │ ├── div_by_minus_one_vm.out │ │ ├── fallback_get_recursion.nut │ │ ├── fallback_get_recursion.out │ │ ├── fuzzer_seed_106.nut │ │ ├── fuzzer_seed_106.out │ │ ├── fuzzer_seed_3250.nut │ │ ├── fuzzer_seed_3250.out │ │ ├── fuzzer_seed_7200.nut │ │ ├── fuzzer_seed_7200.out │ │ ├── import_correct.nut │ │ ├── import_correct.out │ │ ├── inexpr_block/ │ │ │ ├── inexpr_block_1.nut │ │ │ ├── inexpr_block_1.out │ │ │ ├── inexpr_block_2.nut │ │ │ ├── inexpr_block_2.out │ │ │ ├── inexpr_block_3.nut │ │ │ ├── inexpr_block_3.out │ │ │ ├── inexpr_block_4.nut │ │ │ ├── inexpr_block_4.out │ │ │ ├── inexpr_block_5.nut │ │ │ ├── inexpr_block_5.out │ │ │ ├── inexpr_block_6.nut │ │ │ ├── inexpr_block_6.out │ │ │ ├── inexpr_block_7.nut │ │ │ ├── inexpr_block_7.out │ │ │ ├── inexpr_block_8.nut │ │ │ ├── inexpr_block_8.out │ │ │ ├── test_codeblock.nut │ │ │ ├── test_codeblock.out │ │ │ ├── test_codeblock_return_in_try.nut │ │ │ ├── test_codeblock_return_in_try.out │ │ │ ├── test_codeblock_try.nut │ │ │ └── test_codeblock_try.out │ │ ├── integers.nut │ │ ├── integers.out │ │ ├── is_frozen.nut │ │ ├── is_frozen.out │ │ ├── locals_chain.nut │ │ ├── locals_chain.out │ │ ├── metamethod_error.nut │ │ ├── metamethod_error.out │ │ ├── mod64_by_minus_one_opt.nut │ │ ├── mod64_by_minus_one_opt.out │ │ ├── mod64_by_minus_one_vm.nut │ │ ├── mod64_by_minus_one_vm.out │ │ ├── mod_by_minus_one_opt.nut │ │ ├── mod_by_minus_one_opt.out │ │ ├── mod_by_minus_one_vm.nut │ │ ├── mod_by_minus_one_vm.out │ │ ├── native_fields.nut │ │ ├── native_fields.out │ │ ├── opt/ │ │ │ ├── fuzz_52807_min.nut │ │ │ ├── fuzz_52807_min.out │ │ │ ├── fuzz_min.nut │ │ │ ├── fuzz_min.out │ │ │ ├── modify_local_var.nut │ │ │ ├── modify_local_var.out │ │ │ ├── opt_reassign_addi.nut │ │ │ ├── opt_reassign_addi.out │ │ │ ├── opt_reassign_arith.nut │ │ │ ├── opt_reassign_arith.out │ │ │ ├── opt_reassign_chain.nut │ │ │ ├── opt_reassign_chain.out │ │ │ ├── opt_reassign_in_scope.nut │ │ │ ├── opt_reassign_in_scope.out │ │ │ ├── opt_same_reg_fold.nut │ │ │ ├── opt_same_reg_fold.out │ │ │ ├── sqf916086.nut │ │ │ └── sqf916086.out │ │ ├── optimizer.nut │ │ ├── optimizer.out │ │ ├── optimizer_add.nut │ │ ├── optimizer_add.out │ │ ├── optimizer_mul.nut │ │ ├── optimizer_mul.out │ │ ├── parenCallee.nut │ │ ├── parenCallee.out │ │ ├── ph_optimizer_null_call_1.nut │ │ ├── ph_optimizer_null_call_1.out │ │ ├── ph_optimizer_null_call_2.nut │ │ ├── ph_optimizer_null_call_2.out │ │ ├── ph_optimizer_null_call_3.nut │ │ ├── ph_optimizer_null_call_3.out │ │ ├── runtime_type_check/ │ │ │ ├── assign_type_01.nut │ │ │ ├── assign_type_01.out │ │ │ ├── assign_type_02.nut │ │ │ ├── assign_type_02.out │ │ │ ├── assign_type_03.nut │ │ │ ├── assign_type_03.out │ │ │ ├── assign_type_04.nut │ │ │ ├── assign_type_04.out │ │ │ ├── assign_type_05.nut │ │ │ ├── assign_type_05.out │ │ │ ├── assign_type_06.nut │ │ │ ├── assign_type_06.out │ │ │ ├── assign_type_07.nut │ │ │ ├── assign_type_07.out │ │ │ ├── assign_type_08.nut │ │ │ ├── assign_type_08.out │ │ │ ├── assign_type_09.nut │ │ │ ├── assign_type_09.out │ │ │ ├── assign_type_10.nut │ │ │ ├── assign_type_10.out │ │ │ ├── assign_wrong_type_01.nut │ │ │ ├── assign_wrong_type_01.out │ │ │ ├── assign_wrong_type_02.nut │ │ │ ├── assign_wrong_type_02.out │ │ │ ├── assign_wrong_type_03.nut │ │ │ ├── assign_wrong_type_03.out │ │ │ ├── assign_wrong_type_04.nut │ │ │ ├── assign_wrong_type_04.out │ │ │ ├── assign_wrong_type_05.nut │ │ │ ├── assign_wrong_type_05.out │ │ │ ├── assign_wrong_type_06.nut │ │ │ ├── assign_wrong_type_06.out │ │ │ ├── assign_wrong_type_07.nut │ │ │ ├── assign_wrong_type_07.out │ │ │ ├── assign_wrong_type_08.nut │ │ │ ├── assign_wrong_type_08.out │ │ │ ├── assign_wrong_type_09.nut │ │ │ ├── assign_wrong_type_09.out │ │ │ ├── assign_wrong_type_10.nut │ │ │ ├── assign_wrong_type_10.out │ │ │ ├── return_type.nut │ │ │ ├── return_type.out │ │ │ ├── return_wrong_type.nut │ │ │ └── return_wrong_type.out │ │ ├── spec/ │ │ │ ├── class.nut │ │ │ ├── class.out │ │ │ ├── classInher.nut │ │ │ ├── classInher.out │ │ │ ├── class_extend.nut │ │ │ ├── class_extend.out │ │ │ ├── clone.nut │ │ │ ├── clone.out │ │ │ ├── closure.nut │ │ │ ├── closure.out │ │ │ ├── conditionalFor.nut │ │ │ ├── conditionalFor.out │ │ │ ├── const.nut │ │ │ ├── const.out │ │ │ ├── constFolding.nut │ │ │ ├── constFolding.out │ │ │ ├── constFoldingCond.nut │ │ │ ├── constFoldingCond.out │ │ │ ├── const_func.nut │ │ │ ├── const_func.out │ │ │ ├── const_func_freevars.nut │ │ │ ├── const_func_freevars.out │ │ │ ├── const_math_eval.nut │ │ │ ├── const_math_eval.out │ │ │ ├── const_with_expr.nut │ │ │ ├── const_with_expr.out │ │ │ ├── delegate_get.nut │ │ │ ├── delegate_get.out │ │ │ ├── destruct.nut │ │ │ ├── destruct.out │ │ │ ├── dowstmt.nut │ │ │ ├── dowstmt.out │ │ │ ├── enums.nut │ │ │ ├── enums.out │ │ │ ├── foreachstmt.nut │ │ │ ├── foreachstmt.out │ │ │ ├── forstmt.nut │ │ │ ├── forstmt.out │ │ │ ├── func_pure_attr.nut │ │ │ ├── func_pure_attr.out │ │ │ ├── generators.nut │ │ │ ├── generators.out │ │ │ ├── ifstmt.nut │ │ │ ├── ifstmt.out │ │ │ ├── sort.nut │ │ │ ├── sort.out │ │ │ ├── stringtmplt.nut │ │ │ ├── stringtmplt.out │ │ │ ├── trystmt.nut │ │ │ ├── trystmt.out │ │ │ ├── whilestmt.nut │ │ │ └── whilestmt.out │ │ ├── stack_metamethod.nut │ │ ├── stack_metamethod.out │ │ ├── stack_metamethod_few_args.nut │ │ ├── stack_metamethod_few_args.out │ │ ├── stack_metamethod_locals.nut │ │ ├── stack_metamethod_locals.out │ │ ├── staticmemo/ │ │ │ ├── no_memo_mutable_args.nut │ │ │ ├── no_memo_mutable_args.out │ │ │ ├── no_memo_mutable_result.nut │ │ │ ├── no_memo_mutable_result.out │ │ │ ├── static_assign_itself.nut │ │ │ ├── static_assign_itself.out │ │ │ ├── static_exception.nut │ │ │ ├── static_exception.out │ │ │ ├── static_freeze.nut │ │ │ ├── static_freeze.out │ │ │ ├── static_loop.nut │ │ │ ├── static_loop.out │ │ │ ├── static_nested.nut │ │ │ ├── static_nested.out │ │ │ ├── static_opt.nut │ │ │ ├── static_opt.out │ │ │ ├── static_opt2.nut │ │ │ ├── static_opt2.out │ │ │ ├── static_reset1.nut │ │ │ ├── static_reset1.out │ │ │ ├── static_reset2.nut │ │ │ ├── static_reset2.out │ │ │ ├── static_reset_module.nut │ │ │ ├── static_reset_module.out │ │ │ ├── static_tables.nut │ │ │ ├── static_tables.out │ │ │ ├── static_types.nut │ │ │ └── static_types.out │ │ ├── stdlib/ │ │ │ ├── blob_methods.nut │ │ │ ├── blob_methods.out │ │ │ ├── copy_content_with_replace.nut │ │ │ ├── copy_content_with_replace.out │ │ │ ├── datetime.nut │ │ │ ├── datetime.out │ │ │ ├── debug.nut │ │ │ ├── debug.out │ │ │ ├── debug_extras.nut │ │ │ ├── debug_extras.out │ │ │ ├── deep_hash.nut │ │ │ ├── deep_hash.out │ │ │ ├── deep_regex.nut │ │ │ ├── deep_regex.out │ │ │ ├── delegates.nut │ │ │ ├── delegates.out │ │ │ ├── deser_oom.nut │ │ │ ├── deser_oom.out │ │ │ ├── docstring.nut │ │ │ ├── docstring.out │ │ │ ├── file_io.nut │ │ │ ├── file_io.out │ │ │ ├── math_funcs.nut │ │ │ ├── math_funcs.out │ │ │ ├── math_min_max_clamp.nut │ │ │ ├── math_min_max_clamp.out │ │ │ ├── obj_serialization.nut │ │ │ ├── obj_serialization.out │ │ │ ├── obj_serialization_errors.nut │ │ │ ├── obj_serialization_errors.out │ │ │ ├── obj_serialization_errors_arg.nut │ │ │ ├── obj_serialization_errors_arg.out │ │ │ ├── obj_serialization_valid.nut │ │ │ ├── obj_serialization_valid.out │ │ │ ├── rawdelete.nut │ │ │ ├── rawdelete.out │ │ │ ├── regexp.nut │ │ │ ├── regexp.out │ │ │ ├── regexp_fixed_bugs.nut │ │ │ ├── regexp_fixed_bugs.out │ │ │ ├── stream_methods.nut │ │ │ ├── stream_methods.out │ │ │ ├── string.nut │ │ │ ├── string.out │ │ │ ├── string_escape.nut │ │ │ ├── string_escape.out │ │ │ ├── string_format.nut │ │ │ ├── string_format.out │ │ │ ├── swap.nut │ │ │ ├── swap.out │ │ │ ├── swap_stack_check.nut │ │ │ ├── swap_stack_check.out │ │ │ ├── system_lib.nut │ │ │ └── system_lib.out │ │ ├── string_interpolation.nut │ │ ├── string_interpolation.out │ │ ├── string_interpolation_new.nut │ │ ├── string_interpolation_new.out │ │ ├── string_methods.nut │ │ ├── string_methods.out │ │ ├── sub_int_min.nut │ │ ├── sub_int_min.out │ │ ├── surprise_js_dev.nut │ │ ├── surprise_js_dev.out │ │ ├── table_methods.nut │ │ ├── table_methods.out │ │ ├── testNullPropagation.nut │ │ ├── testNullPropagation.out │ │ ├── test_class_yield_call.nut │ │ ├── test_class_yield_call.out │ │ ├── test_shift.nut │ │ ├── test_shift.out │ │ ├── test_stale_stkbase.nut │ │ ├── test_stale_stkbase.out │ │ ├── tostring_recursion.nut │ │ ├── tostring_recursion.out │ │ ├── type_classes/ │ │ │ ├── test_builtin_constructors.nut │ │ │ ├── test_builtin_constructors.out │ │ │ ├── test_inheritance_error.nut │ │ │ ├── test_inheritance_error.out │ │ │ ├── test_unified_types.nut │ │ │ └── test_unified_types.out │ │ ├── type_hints/ │ │ │ ├── function_types.nut │ │ │ ├── function_types.out │ │ │ ├── var_decl.nut │ │ │ └── var_decl.out │ │ ├── type_inference/ │ │ │ ├── test_arithmetic_ok.nut │ │ │ ├── test_arithmetic_ok.out │ │ │ ├── test_array_literal_ok.nut │ │ │ ├── test_array_literal_ok.out │ │ │ ├── test_assignment_chain_ok.nut │ │ │ ├── test_assignment_chain_ok.out │ │ │ ├── test_call_result_ok.nut │ │ │ ├── test_call_result_ok.out │ │ │ ├── test_call_union_return_ok.nut │ │ │ ├── test_call_union_return_ok.out │ │ │ ├── test_class_literal_ok.nut │ │ │ ├── test_class_literal_ok.out │ │ │ ├── test_comparison_ok.nut │ │ │ ├── test_comparison_ok.out │ │ │ ├── test_complex_assign_ok.nut │ │ │ ├── test_complex_assign_ok.out │ │ │ ├── test_complex_bitwise_ok.nut │ │ │ ├── test_complex_bitwise_ok.out │ │ │ ├── test_complex_call_chain_ok.nut │ │ │ ├── test_complex_call_chain_ok.out │ │ │ ├── test_complex_deep_nesting_ok.nut │ │ │ ├── test_complex_deep_nesting_ok.out │ │ │ ├── test_complex_logical_ok.nut │ │ │ ├── test_complex_logical_ok.out │ │ │ ├── test_complex_mixed_arithmetic_ok.nut │ │ │ ├── test_complex_mixed_arithmetic_ok.out │ │ │ ├── test_complex_nested_ternary_ok.nut │ │ │ ├── test_complex_nested_ternary_ok.out │ │ │ ├── test_complex_nullcoalesce_ok.nut │ │ │ ├── test_complex_nullcoalesce_ok.out │ │ │ ├── test_complex_pure_chain_ok.nut │ │ │ ├── test_complex_pure_chain_ok.out │ │ │ ├── test_complex_return_expr_ok.nut │ │ │ ├── test_complex_return_expr_ok.out │ │ │ ├── test_complex_table_ternary_ok.nut │ │ │ ├── test_complex_table_ternary_ok.out │ │ │ ├── test_const_array4_ok.nut │ │ │ ├── test_const_array4_ok.out │ │ │ ├── test_const_array_ok.nut │ │ │ ├── test_const_array_ok.out │ │ │ ├── test_const_function_ok.nut │ │ │ ├── test_const_function_ok.out │ │ │ ├── test_const_inline_ok.nut │ │ │ ├── test_const_inline_ok.out │ │ │ ├── test_const_table_ok.nut │ │ │ ├── test_const_table_ok.out │ │ │ ├── test_destructure_array_default_ok.nut │ │ │ ├── test_destructure_array_default_ok.out │ │ │ ├── test_destructure_array_ok.nut │ │ │ ├── test_destructure_array_ok.out │ │ │ ├── test_destructure_from_function_ok.nut │ │ │ ├── test_destructure_from_function_ok.out │ │ │ ├── test_destructure_nested_ok.nut │ │ │ ├── test_destructure_nested_ok.out │ │ │ ├── test_destructure_table_ok.nut │ │ │ ├── test_destructure_table_ok.out │ │ │ ├── test_freeze_ok.nut │ │ │ ├── test_freeze_ok.out │ │ │ ├── test_function_literal_ok.nut │ │ │ ├── test_function_literal_ok.out │ │ │ ├── test_instance_from_class_ok.nut │ │ │ ├── test_instance_from_class_ok.out │ │ │ ├── test_literal_ok.nut │ │ │ ├── test_literal_ok.out │ │ │ ├── test_return_literal_ok.nut │ │ │ ├── test_return_literal_ok.out │ │ │ ├── test_string_concat_ok.nut │ │ │ ├── test_string_concat_ok.out │ │ │ ├── test_table_literal_ok.nut │ │ │ ├── test_table_literal_ok.out │ │ │ ├── test_table_return_ok.nut │ │ │ ├── test_table_return_ok.out │ │ │ ├── test_ternary_ok.nut │ │ │ ├── test_ternary_ok.out │ │ │ ├── test_typeof_ok.nut │ │ │ └── test_typeof_ok.out │ │ ├── valid_syntax/ │ │ │ ├── arrays.nut │ │ │ ├── arrays.out │ │ │ ├── classes.nut │ │ │ ├── classes.out │ │ │ ├── closures_scope.nut │ │ │ ├── closures_scope.out │ │ │ ├── control_flow.nut │ │ │ ├── control_flow.out │ │ │ ├── destructuring.nut │ │ │ ├── destructuring.out │ │ │ ├── enums_consts.nut │ │ │ ├── enums_consts.out │ │ │ ├── error_handling.nut │ │ │ ├── error_handling.out │ │ │ ├── functions.nut │ │ │ ├── functions.out │ │ │ ├── generators.nut │ │ │ ├── generators.out │ │ │ ├── literals.nut │ │ │ ├── literals.out │ │ │ ├── metamethods.nut │ │ │ ├── metamethods.out │ │ │ ├── misc.nut │ │ │ ├── misc.out │ │ │ ├── null_safety.nut │ │ │ ├── null_safety.out │ │ │ ├── operators.nut │ │ │ ├── operators.out │ │ │ ├── static_memo.nut │ │ │ ├── static_memo.out │ │ │ ├── strings.nut │ │ │ ├── strings.out │ │ │ ├── tables.nut │ │ │ ├── tables.out │ │ │ ├── threads.nut │ │ │ ├── threads.out │ │ │ ├── type_annotations.nut │ │ │ ├── type_annotations.out │ │ │ ├── variables.nut │ │ │ └── variables.out │ │ └── weird/ │ │ ├── 2000_args.nut │ │ ├── 2000_args.out │ │ ├── assign_to_optional_1.nut │ │ ├── assign_to_optional_1.out │ │ ├── assign_to_optional_2.nut │ │ ├── assign_to_optional_2.out │ │ ├── assign_to_optional_3.nut │ │ ├── assign_to_optional_3.out │ │ ├── many_locals.nut │ │ └── many_locals.out │ ├── proposed_optimizations/ │ │ ├── clone_newslot_vs_merge.nut │ │ ├── filter_map_folding.nut │ │ ├── filter_map_folding2.nut │ │ └── strings_folding.nut │ ├── static_analyzer/ │ │ ├── .sqconfig │ │ ├── 200_nullc.diag.txt │ │ ├── 200_nullc.nut │ │ ├── function_rt_detect.diag.txt │ │ ├── function_rt_detect.nut │ │ ├── logic_ops_paren.diag.txt │ │ ├── logic_ops_paren.nut │ │ ├── module_foo.diag.txt │ │ ├── module_foo.nut │ │ ├── nullcheck_ternary.diag.txt │ │ ├── nullcheck_ternary.nut │ │ ├── pattern_class_check.diag.txt │ │ ├── pattern_class_check.nut │ │ ├── pattern_effect_from_call.diag.txt │ │ ├── pattern_effect_from_call.nut │ │ ├── pattern_forloop_merge.diag.txt │ │ ├── pattern_forloop_merge.nut │ │ ├── pattern_intersected_assignment.diag.txt │ │ ├── pattern_intersected_assignment.nut │ │ ├── pattern_lambdas.diag.txt │ │ ├── pattern_lambdas.nut │ │ ├── pattern_loop_state.diag.txt │ │ ├── pattern_loop_state.nut │ │ ├── pattern_param_check.diag.txt │ │ ├── pattern_param_check.nut │ │ ├── w190.diag.txt │ │ ├── w190.nut │ │ ├── w192.diag.txt │ │ ├── w192.nut │ │ ├── w200.diag.txt │ │ ├── w200.nut │ │ ├── w200_3wcmp.diag.txt │ │ ├── w200_3wcmp.nut │ │ ├── w200_arith.diag.txt │ │ ├── w200_arith.nut │ │ ├── w200_arith_deep.diag.txt │ │ ├── w200_arith_deep.nut │ │ ├── w200_arith_plus_eq.diag.txt │ │ ├── w200_arith_plus_eq.nut │ │ ├── w200_special_func_name.diag.txt │ │ ├── w200_special_func_name.nut │ │ ├── w200_static_memo_expr.diag.txt │ │ ├── w200_static_memo_expr.nut │ │ ├── w200_stringconcat.diag.txt │ │ ├── w200_stringconcat.nut │ │ ├── w203.diag.txt │ │ ├── w203.nut │ │ ├── w204.diag.txt │ │ ├── w204.nut │ │ ├── w205-2.diag.txt │ │ ├── w205-2.nut │ │ ├── w205.diag.txt │ │ ├── w205.nut │ │ ├── w206.diag.txt │ │ ├── w206.nut │ │ ├── w206_arith.diag.txt │ │ ├── w206_arith.nut │ │ ├── w208.diag.txt │ │ ├── w208.nut │ │ ├── w209.diag.txt │ │ ├── w209.nut │ │ ├── w210.diag.txt │ │ ├── w210.nut │ │ ├── w210_complex.diag.txt │ │ ├── w210_complex.nut │ │ ├── w210_deep.diag.txt │ │ ├── w210_deep.nut │ │ ├── w210_def.diag.txt │ │ ├── w210_def.nut │ │ ├── w211.diag.txt │ │ ├── w211.nut │ │ ├── w212.diag.txt │ │ ├── w212.nut │ │ ├── w213.diag.txt │ │ ├── w213.nut │ │ ├── w214.diag.txt │ │ ├── w214.nut │ │ ├── w214_static_memo_expr.diag.txt │ │ ├── w214_static_memo_expr.nut │ │ ├── w215.diag.txt │ │ ├── w215.nut │ │ ├── w215_nullc.diag.txt │ │ ├── w215_nullc.nut │ │ ├── w216.diag.txt │ │ ├── w216.nut │ │ ├── w217_break.diag.txt │ │ ├── w217_break.nut │ │ ├── w217_complex.diag.txt │ │ ├── w217_complex.nut │ │ ├── w217_cond_cont.diag.txt │ │ ├── w217_cond_cont.nut │ │ ├── w217_continue.diag.txt │ │ ├── w217_continue.nut │ │ ├── w217_ret.diag.txt │ │ ├── w217_ret.nut │ │ ├── w217_throw.diag.txt │ │ ├── w217_throw.nut │ │ ├── w220.diag.txt │ │ ├── w220.nut │ │ ├── w220_deep.diag.txt │ │ ├── w220_deep.nut │ │ ├── w221.diag.txt │ │ ├── w221.nut │ │ ├── w221_delete.diag.txt │ │ ├── w221_delete.nut │ │ ├── w222.diag.txt │ │ ├── w222.nut │ │ ├── w222_deep.diag.txt │ │ ├── w222_deep.nut │ │ ├── w222_inside_detructure.diag.txt │ │ ├── w222_inside_detructure.nut │ │ ├── w223.diag.txt │ │ ├── w223.nut │ │ ├── w223_method_is.diag.txt │ │ ├── w223_method_is.nut │ │ ├── w224_then.diag.txt │ │ ├── w224_then.nut │ │ ├── w224_while.diag.txt │ │ ├── w224_while.nut │ │ ├── w225.diag.txt │ │ ├── w225.nut │ │ ├── w225_empty_stmt.diag.txt │ │ ├── w225_empty_stmt.nut │ │ ├── w225_switch.diag.txt │ │ ├── w225_switch.nut │ │ ├── w226.diag.txt │ │ ├── w226.nut │ │ ├── w227.diag.txt │ │ ├── w227.nut │ │ ├── w227_external.diag.txt │ │ ├── w227_external.nut │ │ ├── w227_fn_with_same_param.diag.txt │ │ ├── w227_fn_with_same_param.nut │ │ ├── w227_foreach_destr_shadow.diag.txt │ │ ├── w227_foreach_destr_shadow.nut │ │ ├── w227_let_init_fun.diag.txt │ │ ├── w227_let_init_fun.nut │ │ ├── w227_table.diag.txt │ │ ├── w227_table.nut │ │ ├── w227_varargs.diag.txt │ │ ├── w227_varargs.nut │ │ ├── w228.diag.txt │ │ ├── w228.nut │ │ ├── w228_2.diag.txt │ │ ├── w228_2.nut │ │ ├── w228_3.diag.txt │ │ ├── w228_3.nut │ │ ├── w228_4.diag.txt │ │ ├── w228_4.nut │ │ ├── w228_foreach_destr_default_uses.diag.txt │ │ ├── w228_foreach_destr_default_uses.nut │ │ ├── w228_table.diag.txt │ │ ├── w228_table.nut │ │ ├── w228_trivial.diag.txt │ │ ├── w228_trivial.nut │ │ ├── w229.diag.txt │ │ ├── w229.nut │ │ ├── w230_unused_import.diag.txt │ │ ├── w230_unused_import.nut │ │ ├── w231.diag.txt │ │ ├── w231.nut │ │ ├── w232_cascade.diag.txt │ │ ├── w232_cascade.nut │ │ ├── w232_false.diag.txt │ │ ├── w232_false.nut │ │ ├── w232_lambda.diag.txt │ │ ├── w232_lambda.nut │ │ ├── w232_not.diag.txt │ │ ├── w232_not.nut │ │ ├── w232_ter.diag.txt │ │ ├── w232_ter.nut │ │ ├── w232_ternary.diag.txt │ │ ├── w232_ternary.nut │ │ ├── w232_true.diag.txt │ │ ├── w232_true.nut │ │ ├── w233.diag.txt │ │ ├── w233.nut │ │ ├── w234.diag.txt │ │ ├── w234.nut │ │ ├── w234_outer.diag.txt │ │ ├── w234_outer.nut │ │ ├── w235.diag.txt │ │ ├── w235.nut │ │ ├── w236.diag.txt │ │ ├── w236.nut │ │ ├── w238_heuristic.diag.txt │ │ ├── w238_heuristic.nut │ │ ├── w238_idname.diag.txt │ │ ├── w238_idname.nut │ │ ├── w238_isis.diag.txt │ │ ├── w238_isis.nut │ │ ├── w238_merge.diag.txt │ │ ├── w238_merge.nut │ │ ├── w238_sqconfig.diag.txt │ │ ├── w238_sqconfig.nut │ │ ├── w239.diag.txt │ │ ├── w239.nut │ │ ├── w239_sqconfig.diag.txt │ │ ├── w239_sqconfig.nut │ │ ├── w240.diag.txt │ │ ├── w240.nut │ │ ├── w241.diag.txt │ │ ├── w241.nut │ │ ├── w241_conditional.diag.txt │ │ ├── w241_conditional.nut │ │ ├── w244.diag.txt │ │ ├── w244.nut │ │ ├── w248.diag.txt │ │ ├── w248.nut │ │ ├── w248_access.diag.txt │ │ ├── w248_access.nut │ │ ├── w248_additional.diag.txt │ │ ├── w248_additional.nut │ │ ├── w248_andand.diag.txt │ │ ├── w248_andand.nut │ │ ├── w248_andor.diag.txt │ │ ├── w248_andor.nut │ │ ├── w248_array.diag.txt │ │ ├── w248_array.nut │ │ ├── w248_assert.diag.txt │ │ ├── w248_assert.nut │ │ ├── w248_chain.diag.txt │ │ ├── w248_chain.nut │ │ ├── w248_complex2.diag.txt │ │ ├── w248_complex2.nut │ │ ├── w248_complex_key.diag.txt │ │ ├── w248_complex_key.nut │ │ ├── w248_complexcond.diag.txt │ │ ├── w248_complexcond.nut │ │ ├── w248_eq_get.diag.txt │ │ ├── w248_eq_get.nut │ │ ├── w248_evaled.diag.txt │ │ ├── w248_evaled.nut │ │ ├── w248_getfield.diag.txt │ │ ├── w248_getfield.nut │ │ ├── w248_in.diag.txt │ │ ├── w248_in.nut │ │ ├── w248_in_container.diag.txt │ │ ├── w248_in_container.nut │ │ ├── w248_not.diag.txt │ │ ├── w248_not.nut │ │ ├── w248_nullc_2.diag.txt │ │ ├── w248_nullc_2.nut │ │ ├── w248_nullc_3.diag.txt │ │ ├── w248_nullc_3.nut │ │ ├── w248_oror.diag.txt │ │ ├── w248_oror.nut │ │ ├── w248_oror2.diag.txt │ │ ├── w248_oror2.nut │ │ ├── w248_override.diag.txt │ │ ├── w248_override.nut │ │ ├── w248_relative_pred.diag.txt │ │ ├── w248_relative_pred.nut │ │ ├── w248_special_name_func.diag.txt │ │ ├── w248_special_name_func.nut │ │ ├── w248_terminated_branch1.diag.txt │ │ ├── w248_terminated_branch1.nut │ │ ├── w248_tyopeof1.diag.txt │ │ ├── w248_tyopeof1.nut │ │ ├── w248_tyopeof2.diag.txt │ │ ├── w248_tyopeof2.nut │ │ ├── w248_type_func.diag.txt │ │ ├── w248_type_func.nut │ │ ├── w248_while_cond.diag.txt │ │ ├── w248_while_cond.nut │ │ ├── w250_array.diag.txt │ │ ├── w250_array.nut │ │ ├── w250_container.diag.txt │ │ ├── w250_container.nut │ │ ├── w254.diag.txt │ │ ├── w254.nut │ │ ├── w254_instanceof.diag.txt │ │ ├── w254_instanceof.nut │ │ ├── w254_notin.diag.txt │ │ ├── w254_notin.nut │ │ ├── w255.diag.txt │ │ ├── w255.nut │ │ ├── w255_2.diag.txt │ │ ├── w255_2.nut │ │ ├── w255_foreach_destr_distinct.diag.txt │ │ ├── w255_foreach_destr_distinct.nut │ │ ├── w256.diag.txt │ │ ├── w256.nut │ │ ├── w257.diag.txt │ │ ├── w257.nut │ │ ├── w258.diag.txt │ │ ├── w258.nut │ │ ├── w258_2.diag.txt │ │ ├── w258_2.nut │ │ ├── w259.diag.txt │ │ ├── w259.nut │ │ ├── w260_local_function.diag.txt │ │ ├── w260_local_function.nut │ │ ├── w260_table.diag.txt │ │ ├── w260_table.nut │ │ ├── w260_table_sqconfig.diag.txt │ │ ├── w260_table_sqconfig.nut │ │ ├── w262.diag.txt │ │ ├── w262.nut │ │ ├── w263.diag.txt │ │ ├── w263.nut │ │ ├── w264.diag.txt │ │ ├── w264.nut │ │ ├── w266.diag.txt │ │ ├── w266.nut │ │ ├── w267.diag.txt │ │ ├── w267.nut │ │ ├── w269.diag.txt │ │ ├── w269.nut │ │ ├── w270.diag.txt │ │ ├── w270.nut │ │ ├── w271.diag.txt │ │ ├── w271.nut │ │ ├── w272.diag.txt │ │ ├── w272.nut │ │ ├── w275.diag.txt │ │ ├── w275.nut │ │ ├── w275_all_variants.diag.txt │ │ ├── w275_all_variants.nut │ │ ├── w275_complex.diag.txt │ │ ├── w275_complex.nut │ │ ├── w277.diag.txt │ │ ├── w277.nut │ │ ├── w279_1.diag.txt │ │ ├── w279_1.nut │ │ ├── w279_2.diag.txt │ │ ├── w279_2.nut │ │ ├── w280.diag.txt │ │ ├── w280.nut │ │ ├── w281.diag.txt │ │ ├── w281.nut │ │ ├── w283.diag.txt │ │ ├── w283.nut │ │ ├── w284.diag.txt │ │ ├── w284.nut │ │ ├── w285.diag.txt │ │ ├── w285.nut │ │ ├── w286.diag.txt │ │ ├── w286.nut │ │ ├── w286_2.diag.txt │ │ ├── w286_2.nut │ │ ├── w286_oror_andand.diag.txt │ │ ├── w286_oror_andand.nut │ │ ├── w287.diag.txt │ │ ├── w287.nut │ │ ├── w288.diag.txt │ │ ├── w288.nut │ │ ├── w288_dp_va.diag.txt │ │ ├── w288_dp_va.nut │ │ ├── w288_lambdas2.diag.txt │ │ ├── w288_lambdas2.nut │ │ ├── w288_native.diag.txt │ │ ├── w288_native.nut │ │ ├── w288_require.diag.txt │ │ ├── w288_require.nut │ │ ├── w288_require_indirect.diag.txt │ │ ├── w288_require_indirect.nut │ │ ├── w289.diag.txt │ │ ├── w289.nut │ │ ├── w291.diag.txt │ │ ├── w291.nut │ │ ├── w292.diag.txt │ │ ├── w292.nut │ │ ├── w293.diag.txt │ │ ├── w293.nut │ │ ├── w295.diag.txt │ │ ├── w295.nut │ │ ├── w297.diag.txt │ │ ├── w297.nut │ │ ├── w305.diag.txt │ │ ├── w305.nut │ │ ├── w306.diag.txt │ │ ├── w306.nut │ │ ├── w308.diag.txt │ │ ├── w308.nut │ │ ├── w309_require.diag.txt │ │ ├── w309_require.nut │ │ ├── w310_require.diag.txt │ │ ├── w310_require.nut │ │ ├── w312_require.diag.txt │ │ ├── w312_require.nut │ │ ├── w318_merge_empty_table.diag.txt │ │ ├── w318_merge_empty_table.nut │ │ ├── w319_empty_array_resize.diag.txt │ │ ├── w319_empty_array_resize.nut │ │ ├── w320.diag.txt │ │ ├── w320.nut │ │ ├── w321.diag.txt │ │ ├── w321.nut │ │ ├── w322.diag.txt │ │ └── w322.nut │ └── types/ │ ├── default_values.nut.txt │ ├── default_values.out │ ├── default_values_invalid.nut.txt │ ├── default_values_invalid.out │ ├── invalid.nut.txt │ ├── invalid.out │ ├── type_suggestions.nut.txt │ ├── type_suggestions.out │ ├── valid.nut.txt │ └── valid.out └── testRunner.py ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/workflows/docs_build_deploy.yaml ================================================ name: Docs - build and deploy on: push: branches: - master paths: - "doc/**" workflow_dispatch: jobs: build: runs-on: ubuntu-latest outputs: artifact_name: ${{ steps.artifact_name.outputs.artifact_name }} permissions: contents: read steps: - name: Checkout project uses: actions/checkout@v4 - name: Set up Python 3.11 uses: actions/setup-python@v5 with: python-version: "3.11" cache: "pip" - name: Install dependencies working-directory: ./doc run: python3 -m pip install -r requirements.txt - name: Build documentation with sphinx-build working-directory: ./doc run: sphinx-build -b html -d build/doctrees source build/html - name: Generate artifact name id: artifact_name run: | export AN=docs_html_$(date +'%Y%m%dT%H%M%S') echo Generated artifact name: ${AN} echo "ARTIFACT_NAME=${AN}" >> $GITHUB_ENV echo "artifact_name=${AN}" >> $GITHUB_OUTPUT - name: Upload artifact uses: actions/upload-artifact@v4 with: name: ${{ env.ARTIFACT_NAME }} path: ./doc/build/html retention-days: 1 if-no-files-found: error deploy: needs: build runs-on: ubuntu-latest env: ARTIFACT_NAME: ${{ needs.build.outputs.artifact_name }} permissions: actions: read steps: - name: Download artifact uses: actions/download-artifact@v4 with: name: ${{ env.ARTIFACT_NAME }} path: ./doc - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@v4 with: aws-access-key-id: ${{ secrets.DOC_KEYID }} aws-secret-access-key: ${{ secrets.DOC_SECRET }} aws-region: eu-west-1 - name: Update target run: | aws s3 sync ./doc s3://quirrel.io/doc aws cloudfront create-invalidation --distribution-id ${{ secrets.DOC_DISTID }} --paths "/*" ================================================ FILE: .gitignore ================================================ # Folders created at compilation bin/ lib/ # CMake output build*/ # Folders created at documentation generation doc/build/ Release/ .vs/ *.vcxproj *.vcxproj.filters *.vcxproj.user .vscode/ ================================================ FILE: .travis.yml ================================================ language: cpp compiler: - gcc - clang # Travis VMs are 64-bit but we compile both for 32 and 64 bit. To enable the # 32-bit builds to work, we need gcc-multilib. addons: apt: packages: - gcc-multilib - g++-multilib # Enable container-based builds. sudo: false script: mkdir build && cd build && cmake .. && make -j2 ================================================ FILE: CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.5) project(squirrel VERSION 4.0 LANGUAGES C CXX) option(ENABLE_VAR_TRACE "Enable variable change tracing feature.") if (NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "Release") endif () include(GNUInstallDirs) set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_BINDIR}") set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}") set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY "${PROJECT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}") set(CMAKE_CXX_STANDARD 17) if(CMAKE_COMPILER_IS_GNUCXX OR CMAKE_CXX_COMPILER_ID MATCHES "Clang") add_compile_options( "$<$:-fno-rtti;-fno-exceptions>" -fno-strict-aliasing -Wall -Wextra -pedantic -Wcast-qual -Wno-unused-parameter -Wno-missing-field-initializers "$<$:-O3>" "$<$:-O3;-g>" "$<$:-Os>" ) # Debug flags if(CMAKE_COMPILER_IS_GNUCXX) add_compile_options("$<$:-g3;-Og>") else() # Clang debug flags add_compile_options("$<$:-g;-Og>") endif() elseif(MSVC) set(CMAKE_WINDOWS_EXPORT_ALL_SYMBOLS ON) add_definitions(-D_CRT_SECURE_NO_WARNINGS) add_compile_options(/wd4244 /wd4018 /wd4267) # disable type conversion warnings endif() if(CMAKE_SIZEOF_VOID_P EQUAL 8) add_compile_definitions(_SQ64) endif() add_subdirectory(squirrel) add_subdirectory(squirrel/compiler) add_subdirectory(sqstdlib) add_subdirectory(sqmodules) add_subdirectory(sq) include(CMakePackageConfigHelpers) write_basic_package_version_file( "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/cmake/squirrel/squirrel-config-version.cmake" VERSION "${squirrel_VERSION}" COMPATIBILITY AnyNewerVersion ) configure_package_config_file( "${CMAKE_CURRENT_SOURCE_DIR}/squirrel-config.cmake.in" "${CMAKE_CURRENT_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR}/cmake/squirrel/squirrel-config.cmake" INSTALL_DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/squirrel" ) ================================================ FILE: COPYRIGHT ================================================ Copyright (c) 2003-2017 Alberto Demichelis Copyright (c) 2016-2024 Gaijin Games KFT Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ----------------------------------------------------- END OF COPYRIGHT ================================================ FILE: HISTORY ================================================ ***version 4.5.0*** * add 'let' keyword and unsassignable named bindings ***version 4.4.0*** * base lib: container methods check object immutability * more meaningful error for wrong enum field * put 'stream' class into target table instead of root Also expose expicit sqstd_init_streamclass() to allow placing stream class into specific table * register base lib as compile-time bindings * remove setroottable()/setconsttable() script functions * base lib: add println() and errorln() functions ***version 4.3.0 stable*** * baselib: move some global constants to getbuildinfo() method * always use modules in commandline interpreter * use compile-time bindings for reference modules implementation * check thread-safety during code execution * str.indexof and str.contains now throws error on empty substring. * (bugfix) don't add object to ref table in GetRefCount() * (experimental) new operator ':=' for assignment inside if, for, while, switch ***version 4.2.0 stable*** * make commas in destructuring assignment optional * change constant tables handling by compiler Earlier only enums could be found in compile-time constants. But now we have immutable objects ***version 4.1.0 stable*** -introduced immutable objects -switched to semantic versioning -changed SQUIRREL_VERSION_NUMBER to SQUIRREL_VERSION_NUMBER_MAJOR, SQUIRREL_VERSION_NUMBER_MINOR and SQUIRREL_VERSION_PATCH and removed sq_getversion() because of ambiguity of encoding semantic version into single integer number ***version 4.0.0 stable*** -renamed to Quirrel -bumped version -lots of changes - see diff_from_original_squirrel ***version 3.2 stable*** -added sq_tailcall -added rawcall keyword -added post call initializer syntax -added table.keys() and table.values() -added table.filter() -additional parameters in array.map() and array.apply() -additional optional initializer in array.reduce() -closure.call() is now a "native tailcall" and the invoked function can now be suspended -fixed sq_newmember and sq_rawnewmember properly pop parameters -fixed capturing free variable on for loop counter before a break statement -fixed \u in lexer -various bugfixes ***version 3.1.1 stable*** -sq_gettypetag doesn't set last error(it's treated as SQBool function but keeps a SQRESULT for backward compatibility) -fixed _set method in userdata delegates -fixed some warnings ***version 3.1 stable*** -added slice range for tolower and toupper -added startswith() and endswith() in string lib -added SQ_EXCLUDE_DEFAULT_MEMFUNCTIONS to exclude default mem fuction from compilation -added sq_getreleasehook -added thread.wakeupthrow() -added sq_pushthread -added \u and \U escape sequence for UTF8,UTF16 or UCS4 characters -added CMake scripts(thx Fabian Wolff) -the escape character \x is based on sizeof(SQChar) -fixed several warnings(thx Markus Oberhumer) -fixed optimizer bug in compound arith oprators(+=,-= etc...) -fixed sq_getrefvmcount() (thx Gerrit) -fixed sq_getrefcount() when no references were added with sq_addref() (thx Gerrit) -fixed bug in string.tointeger() (thx Domingo) -fixed weakref comparison in 32bit builds using doubles(thx Domingo) -fixed compiler bug(thx Peter) -fixed some error in the documentation(thx Alexander) -fixed some error reporting in compiler(thx Alexander) -fixed incorrect optional semicolon after "if block"(thx Alexander) -fixed crash bug in compiler related to compound arith operators(+=,-= etc...) (thx Jeff1) ***2015-01-10 *** ***version 3.1 RC 1*** -added new header sqconfig.h for all optional type declarations(unicode, 64bits etc..) -added sq_setsharedforeignptr sq_getsharedforeignptr -added sq_setsharedreleasehook sq_getsharedreleasehook -added escape() in sqstd string library -added __LINE__ and __FILE__ (thx mingodad) -widechar support on gcc builds -now boolean can be used in constants -reduced dependencies on C runtime library -newthread and sq_newthread() no longer reinitialize the root table on friend VMs(thx Lucas Cardellini) -exceptions in the _inherited metamethod are propagated(thx Lucas Cardellini) -'in' operator performance improvement(thx unagipai and mingodad) -fixes crash in compiler when trying to write 'base' -fixed bug in switch statement when using locals as case values (thx mingodad) -fixed bug in print()(thx Lucas Cardellini) ***2013-08-30 *** ***version 3.1 beta 1*** -added new scoping rule(root attached to closures) -added closure.setroot() closure.getroot() -added sq_setclosureroot() and sq_getclosureroot() -added sq_setvmreleasehook() and sq_getvmreleasehook() -added documentaion for sq_getbase() -now string.tointeger() accepts an optional parameter 'base' -now format accepts zeroes in the format string (thx mingodad) -fixed bug in sqstd_createfile() (thx mingodad) -minor buxfixes ***2012-11-10 *** ***version 3.0.4 stable*** -sq_deleteslot slot now pops the key in case of failure -fixed bug when _get metamethod throws null -fixed a bug in rstrip -added some error handling -minor bugfixes ***2012-06-19 *** ***version 3.1.0 alpha 1*** -changed in and instanceof operator precendence -root object in closures -added closure.setroot closure.getroot -added sq_setclosureroot and sq_getclosureroot ***version 3.0.3 stable*** -improved error messages for _cmp(when a non integer value is returned) (thx Yexo) -added class.newmember() built in method (thx Nam) -added class.rawnewmember() built in method (thx Nam) -added sq_rawnewmember() (thx Nam) -added sq_getversion() -added sq_typeof() -added sq_getclosurename() -added file.close() in stdlib -documented closure.getinfos() built-in method -fixed string iteration doesn't return negative numbers for characters > 127 -fixed bug in tofloat() when converting a string with scientific notation without a decimal point (thx wr2) -fixed potential infinite loop in array.sort() when the _cmp function is inconsistent (thx Yexo) -fixed obscure bug in the compiler(thx yishin) -fixed some minor bug ***2011-11-28 *** ***version 3.0.2 stable*** -added sq_gethash API -now array.sort() is implemented with heapsort -now floats in scientific notation also accept numbers with no '.' (eg. 1e+6 or 1e6) -fixed some warning -fixed some documentation -fixed bug in GC ***2011-09-08 *** ***version 3.0.1 stable*** -added # as alternative symbol for "line comment"(mostly useful for shell scripts) -added sq_throwobject() to throw an arbitrary object from the C API -added alignement flag for userdata types, SQ_ALIGNMENT (thx Shigemasa) -added rawset() and rawget() to class and instance default delegate -changed bytecode format now ensures matching integer size and float size -now inherited classes also inherit userdatasize -added SQUIRREL_VERSION_NUMBER in squirrel.h and _versionnumber_ global symbol -fixed sq_getmemberhandle -fixed sq_getrefcount -refactored some sqstdio code -refactored some clone code -refactored some stuff in the string lib -added -s and -fno-exceptions in GCC makefile(better performance when using GCC) ***2011-03-13 *** ***version 3.0 stable*** -added sq_getcallee() -sq_getfreevariable() also works for native closures -minior optimizations -removed several warning when compiling with GCC 4.x -fixed some errors in the documentation -fixed bug when using SQUSEDOUBLE and 32bits intengers -fixed bug when invoking generators with closure.call() (thx huntercool) ***2010-12-19 *** ***version 3.0 release candidate 1(RC 1)*** -improved metamethods error handling -added parameter 'isstatic' to _newmember metamethod(thx G.Meyer) -added sq_getrefcount() to return number of refences from C++(thx G.Meyer) ***2010-11-07 *** ***version 3.0 beta 3*** -license changed to "MIT license" -added sq_resurrectunreachable() and resurrectunreachable() -added callee() built in function, returns the current running closure -added thread.getstackinfos() -added sq_objtouserpointer() -added sq_newtableex() -various refactoring and optimizations -fixed several 64bits issues regarding integer to string conversions -fixed some bugs when SQUSEDOUBLE is used in 32bits systems ***2010-08-18 *** ***version 3.0 beta 2.1*** -fixed bug in class constructor -fixed bug in compound arith ***2010-08-12 *** ***version 3.0 beta 2*** -class methods can be added or replaced after the class as been instantiated -JSON compliant table syntax, this is currently an experimental feature (thx atai) -sq_getsize() now returns userdatasize for classes and instances -now setroottable() and setconsttable() return the previous value of the respective table -fixed bug in compound arith operators when used on a free variable (thx ellon) -fixed some x64 minor bugs -fixed minor bug in the compiler -refactored some VM internals -documented sq_getmemberhandle, sq_getbyhandle, sq_setbyhandle to set and get value from classes ***2009-11-15 *** ***version 3.0 beta 1*** -various refactoring and optimizations -fixed bug in free variables (thx mokehehe) -fixed bug in functions with default parameters (thx ara & Yexo) -fixed bug in exception handling -improved error propagation in _set and _get metamethods ( and 'throw null' for clean failure) -added sq_getmemberhandle, sq_getbyhandle, sq_setbyhandle to set and get value from classes ***2009-06-30 *** ***version 3.0 alpha 2*** -added real free variables(thx Paul Ruizendaal) -added refactored function call implementation and compiler(thx Paul Ruizendaal) -added sq_getfunctioninfo -added compile time flag SQUSEDOUBLE to use double precision floats -added global slot _floatsize_ int the base lib to recognize single precision and double precision builds -sq_wakeupvm can now resume the vm with an exception -added sqstd_format -now blobs can be cloned -generators can now be instantiated by calling sq_call() or closure.call() -fixed debughook bug -fixed cooroutine error propagation ***2008-07-23 *** ***version 3.0 alpha 1*** -first branch from 2.x source tree -added 'base' keyword -removed 'delegate' keyword -now compiled scripts are vararg functions -added setdelegate() and getdelegate() table builtin methods -added <=> 3 ways compare operator -added lambda expression @(a,b) a + b -added local function statement -added array built-in map(),reduce(),apply(),filter() and find() -generators hold only a weak reference of the enviroment object -removed 'vargv' and 'vargc' keywords -now var args are passed as an array called vargv(as a paramter) -removed 'parent' keyword -added class getbase() built in method -instanceof doesn't throw an exception if the left expression is not a class -lexical scoping for free variables(free variables are no longer in the second parameter list) -sq_setprintfunc accept error func -sq_geterrorfunc() -added sq_arrayremove() and sq_arrayinsert() -error() built in function(works like print but prints using the errorfunc) -added native debug hook ***2008-02-17 *** ***version 2.2 stable*** -added _newslot metamethod in classes -added enums added constants -added sq_pushconsttable, sq_setconsttable -added default param -added octal literals(thx Dinosaur) -fixed debug hook, 'calls' and 'returns' are properly notified in the same number. -fixed a coroutine bug ***2007-07-29 *** ***version 2.1.2 stable*** -new behaviour for generators iteration using foreach now when a generator is iterated by foreach the value returned by a 'return val' statement will terminate the iteration but will not be returned as foreach iteration -added sq_setclassudsize() -added sq_clear() -added table.clear(), array.clear() -fixed sq_cmp() (thx jyuill) -fixed minor bugs ***2006-08-21 *** ***version 2.1.1 stable*** -vm refactoring -optimized internal function memory layout -new global symbol _version_ (is the version string) -code size optimization for float literals(on 32bits float builts) -now the raw ref API(sq_addref etc...) is fully reentrant. -fixed a bug in sq_getdelegate() now pushes null if the object doesn't have a delegate(thx MatzeB) -improved C reference performances in NO_GARBAGE_COLLECTOR builds -sq_getlocal() now enumerates also outer values. -fixed regexp library for GCC users. ***2006-03-19 *** ***version 2.1 stable*** -added static class fields, new keyword static -added 64bits architecture support -added global slot _intsize_ int the base lib to recognize 32bits and 64bits builds -added functions with fixed environment, closure.bindenv() built-in function -all types except userdata and null implement the tostring() method -string concatenation now invokes metamethod _tostring -new metamethods for class objects _newmember and _inherited -sq_call() sq_resume() sq_wakeupvm() have a new signature -new C referencing implementation(scales more with the amount of references) -refactored hash table -new api functions sq_newslot(),sq_tobool(),sq_getbase(), sq_instanceof(), sq_bindenv() -the api func sq_createslot was deprecated but still supported in form of C macro on top of sq_newslot -sq_setreleasehook() now also works for classes -stream.readstr() and stream.writestr() have been deprecated(this affects file and blob) -fixed squirrel.h undeclared api calls -fixed few minor bugs -SQChar is now defined as wchar_t -removed warning when building with -Wall -pedantic for GCC users -added new std io function writeclosuretofile() -added new std string functions strip(),rstrip(),lstrip() and split() -regular expressions operators (+,*) now have more POSIX greedyness behaviour -class constructors are now invoked as normal functions ***2005-10-02 *** ***version 2.0.5 stable*** -fixed some 64bits incompatibilities (thx sarge) -fixed minor bug in the stdlib format() function (thx Rick) -fixed a bug in dofile() that was preventing to compile empty files -added new API sq_poptop() & sq_getfreevariable() -some performance improvements ***2005-08-14 *** ***version 2.0.4 stable*** -weak references and related API calls -added sq_objtobool() -class instances memory policies improved(1 mem allocation for the whole instance) -typetags are now declared as SQUserPointer instead of unsigned int -first pass for 64bits compatibility -fixed minor bug in the stdio stream -fixed a bug in format() -fixed bug in string.tointeger() and string.tofloat() ***2005-06-24 *** ***version 2.0.3 stable*** -dofile() and loadfile() in the iolib now can decode ASCII, UTF8 files UCS2 big-endian and little-endian -sq_setparamscheck() : now typemesk can check for null -added string escape sequence \xhhhh -fixed some C++ standard incompatibilities ***2005-05-15 *** ***version 2.0.2 stable*** -performances improvements (expecially for GCC users) -removed all dependencies from C++ exception handling -various bugfixes ***2005-04-12 *** ***version 2.0.1 stable*** -various bugfixes -sq_setparamscheck() now allows spaces in the typemask ***2005-04-03 *** ***version 2.0 stable*** -added API sq_gettypetag() -added built-in function to the bool type(tointeger, tostring etc...) ***2005-02-27 *** ***version 2.0 release candidate 1(RC 1)*** -added API sq_reseterror() -modified sq_release() -now class instances can be cloned -various bufixes ***2005-01-26 *** ***version 2.0 beta 1*** -added bool type -class properties can be redefined in a derived class -added ops *= /= and %= -new syntax for class attributes declaration instead of ( and ) -increased the max number of literals per function from 65535 to 16777215 -now free variables have proper lexical scoping -added API sq_createinstance(), sq_pushbool(), sq_getbool() -added built-in function type() -added built-in function obj.rawin(key) in table,class and instance -sq_rawget() and sq_rawset() now work also on classes and instances -the VM no longer uses C++ exception handling (more suitable for embedded devices) -various bufixes ***2004-12-21 *** ***version 2.0 alpha 2*** -globals scoping changed, now if :: is omitted the VM automatically falls back on the root table -various bufixes -added class level attributes ***2004-12-12 *** ***version 2.0 alpha 1*** -codebase branch from version 1.x -added classes -added functions with variable number of parameters(vargc & vargv and the ...) -0 and 0.0 are now considered 'false' by all conditional statements(if,while,for,?,do-while) -added new api functions sq_newclass() sq_setinstanceup() sq_getinstanceup() sq_getattributes() sq_setattributes() -modified api sq_settypetag() ***2004-11-01 *** ***version 1.0 stable*** -fixed some minor bug -improved operator 'delete' performances -added scientific notation for float numbers( eg. 2.e16 or 2.e-2) ***2004-08-30 *** ***version 1.0 release candidate 2(RC 2)*** -fixed bug in the vm(thx Pierre Renaux) -fixed bug in the optimizer(thx Pierre Renaux) -fixed some bug in the documentation(thx JD) -added new api functions for raw object handling -removed nested multiline comments -reduced memory footprint in C references ***2004-08-23 *** ***version 1.0 release candidate 1(RC 1)*** -fixed division by zero -the 'in' operator and obj.rawget() do not query the default delegate anymore -added function sq_getprintfunc() -added new standard library 'auxlib'(implements default error handlers) ***2004-07-12 *** ***version 1.0 beta 4*** -fixed a bug in the integer.tochar() built-in method -fixed unary minus operator -fixed bug in dofile() -fixed inconsistency between != and == operators(on float/integer comparison) -added javascript style unsigned right shift operator '>>>' -added array(size) constructor built-in function -array.resize(size,[fill]) built-in function accepts an optional 'fill' value -improved debug API, added sq_getclosureinfo() and sq_setnativeclosurename() ***2004-05-23 *** ***version 1.0 beta 3*** -minor vm bug fixes -string allocation is now faster -tables and array memory usage is now less conservative(they shrink) -added regular expression routines in the standard library -The 'c' expression now accepts only 1 character(thx irbrian) -multiline strings <[ ]> have been substituted with C# style verbatim strings (eg. @"string") -added new keyword 'parent' for accessing the delegate of tables and unserdata -The metamethod '_clone' has been renamed '_cloned' -the _delslot metamethod's behaviour and prototype have been changed -new default function in the integer and float object 'tochar()' -the built-in function chcode2string has been removed -the default method [table].getdelegate() has been removed -new api sq_rawdeleteslot() -new table built-in method rawdelete(key) -the dynamic mudule loading has been removed from the standard distribution -some optimizations in the VM ***2004-04-21 *** ***version 1.0 beta 2*** -minor compiler/parser bug fixes -sq_newclosure has a different prototype, the "paramscheck" of paramter has been moved to the new function sq_setparamscheck() -sq_setparamscheck allows to add automatic parameters type checking in native closures -sq_compile() lost the lineinfo parameter -new api sq_enabledebuginfo() globally sets compiler's debug info generation -added consistency check on bytecode serialization -fixed += operator, now works on strings like + -added global slot in the base lib _charsize_ to recognize unicode builds from ascii builds runtime -added registry table -new api call sq_pushregistrytable() -added type tag to the userdata type sq_settypetag() -sq_getuserdata now queries the userdata typetag -the built in function collect_garbage() as been renamed collectgarbage() for consistency reasons -new standard libraries(sqlibs are now obsolete) ***2004-02-20 *** ***version 1.0 beta 1*** -fixed a bug in the compiler (thanks Martin Kofler) -fixed bug in the switch case statement -fixed the _unm metamethod -fixed minor bugs in the API -fixed automatic stack resizing -first beta version first pass code clean up in the VM and base lib first pass code coverege test has been done on VM and built-in lib -new VM creation API sq_open() sq_close() (sq_newvm and sq_releasevm are now obsolete) -new api allows to specifiy a "print" function to output text(sq_printfunc) -added some small optimizations -new cooperative multi-threading capabilities in the base library(coroutines), VMs are now a built in type("thread") -new built in functions have been added for manipulating the new "thread" type -friend virtual machines share the same root table, error handler and debug hook by default -new compile time options ***2004-01-19 *** ***version 0.9 alpha*** -fixed a garbage collection bug -fixed some API bugs(thanks to Joshua Jensen) -fixed tail calls (in the version 0.8 the tail call optimization was erroneously disabled) -new function parameters semantic, now passing a wrong number of parameters generates an exception -native closures have now a built in parameter number checking -sq_rawget and sq_rawset now work also on arrays -sq_getsize now woks also on userdata -the userdata release hook prototype is changed(now passes the size of the userdata) -the lexer reader function now returns an integer instead of a char that allows better error checking on the input(thx Joshua Jensen) -faster compiler -try/catch blocks do not cause any runtime memory allocation anymore ***2003-12-06 *** ***version 0.8 alpha*** -fixed a bug that was preventing to have callable userdata throught the metamethod _call -fixed a garbage collection bug -fixed == operator now can compare correctly different types -new built in method getstackinfos(level) -improved line informations precision for the debug hook -new api call sq_compilebuffer() -new built-in api function compilestring() -new syntactic sugar for function declarations inside tables -the debug API has been finalized ***2003-11-17 *** ***version 0.7 alpha*** -fixed critical bug SQInteger the tail call system -fixed bug in the continue statement code generation -fixed func call param issue(thanks to Rewoonenco Andrew) -added _delslot metamethod(thanks to Rewoonenco Andrew) -new multiline string expression ( delimited by <[ and ]> ) -normal strings ("") do not allow embedded new line anymore -reduced vm memory footprint(C refs are shared between friend VMs) -new api method sq_deleteslot() -new debug hook event 'r' is triggered when a function returns ***2003-11-04 *** ***version 0.6 alpha*** -fixed switch statement(was executing the default case after a break) -sq_call() doesn't pop the closure (just the params) -the vm execution can be suspended from the C API anytime (micro-threads) -new api calls sq_suspendvm() sq_wakeupvm() sq_getvmstate() and sq_reservestack() ***2003-10-13 *** ***version 0.5 alpha*** -fixed some minor bug -tested with non ASCII identifiers in unicode mode(I've tried chinese chars) -added built-in function string.find() -the built-in function array.sort() optionally accepts a cmp(a,b) function -the debug hook function now has a new prototype debug_hook(event_type,sourcefile,line,functionname) -fixed some debug info imprecision ***2003-10-01 *** ***version 0.4 alpha*** -faster VM -sq_call will pop arguments and closure also in case of failure -fixed a bug in sq_remove -now the VM detects delegation cycles(and throws an exception) -new operators ++ and -- -new operator ',' comma operator -fixed some expression precedence issue -fixed bug in sq_arraypop ***2003-09-15 *** ***version 0.3 alpha*** -fixed a bug in array::insert() -optional Unicode core(define SQUNICODE or _UNICODE on Win32) -sq_compiler uses a new reader function SQLEXREADFUNC -the debug hook passes 'l' instead of 'line' for line callbacks and 'c' instead of 'call' for call callbacks -new array.extend() bulit-in function -new API sq_clone() ***2003-09-10 *** ***version 0.2 pre-alpha*** -new completely reentrant VM (sq_open and sq_close are now obsolete) -sq_newvm() has a new prototype -allocators are now global and linked in the VM -_newslot meta method added -rawset creates a slot if doesn't exists -the compiler error callback pass the vm handle(thanks Pierre Renaux) -sq_setforeignptr() sq_getforeingptr() are now public -sq_resume() now is possible to resume generators from C -sq_getlasterror() retrieve the last thrown error -improved docs ***2003-09-06 *** ***version 0.1 pre-alpha*** first release ================================================ FILE: README.md ================================================ # Quirrel Quirrel is a fast, high-level imperative programming language, designed to be a lightweight but powerful scripting tool that fits within the size, memory bandwidth, and real-time requirements of applications like games. It is a mature scripting language used in dozens of games and other apps (including web servers and embeddable platforms) with hundreds of millions of installations. Millions of devices are running Quirrel VMs daily. Quirrel has simple integration and bindings into any C++ application. Quirrel has C/Java/C++-like syntax with a clear, rich and simple type system, but the language has a very dynamic nature like Python/Lua/JavaScript. Easy to start while being much safer than Lua and JS, with equal performance to Lua. The whole [syntax and documentation](http://quirrel.io/doc/reference/language.html) can be read in approximately an hour. It is based on the [Squirrel scripting language](http://www.squirrel-lang.org/), but modified to be safer, stricter and faster. It branched off as a separate language in 2019. Gaijin Entertainment has used Squirrel with modifications since 2006, transitioning to Quirrel since 2019, across all its projects and platforms (X360, PS3, PS4, XBoxOne, Switch, iOS, Android, Windows/Mac/Linux PC). Quirrel offers a wide range of features like dynamic typing, higher order functions, generators, tail recursion, exception handling, coroutines, automatic memory management, modules with hot reload, static code analysis. Feedback, PRs and suggestions are appreciated! [Documentation project page](http://quirrel.io) [GitHub page](https://github.com/GaijinEntertainment/quirrel) ================================================ FILE: appveyor.yml ================================================ version: 0.0.{build} platform: - x86 - x64 configuration: - Debug - Release clone_folder: c:\sq before_build: - mkdir build - cd build - call "C:\Program Files (x86)\Microsoft Visual Studio 14.0\VC\vcvarsall.bat" %platform% - echo %platform% - if %platform%==X64 (cmake .. -G "Visual Studio 14 2015 Win64") - if %platform%==x86 (cmake .. -G "Visual Studio 14 2015") build_script: - cmake --build . --config %configuration% -- /logger:"C:\Program Files\AppVeyor\BuildAgent\Appveyor.MSBuildLogger.dll" artifacts: - path: build\*\%configuration%\*.exe - path: build\*\%configuration%\*.dll test: off ================================================ FILE: doc/.gitignore ================================================ build *.pyc ================================================ FILE: doc/Makefile ================================================ # Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from http://sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source .PHONY: help help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " applehelp to make an Apple Help Book" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" @echo " coverage to run coverage check of the documentation (if enabled)" .PHONY: clean clean: rm -rf $(BUILDDIR)/* .PHONY: html html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." .PHONY: dirhtml dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." .PHONY: singlehtml singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." .PHONY: pickle pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." .PHONY: json json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." .PHONY: htmlhelp htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." .PHONY: qthelp qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/testy_sphinxy.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/testy_sphinxy.qhc" .PHONY: applehelp applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @echo @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." @echo "N.B. You won't be able to view it unless you put it in" \ "~/Library/Documentation/Help or install it in your application" \ "bundle." .PHONY: devhelp devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/testy_sphinxy" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/testy_sphinxy" @echo "# devhelp" .PHONY: epub epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." .PHONY: latex latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." .PHONY: latexpdf latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." .PHONY: latexpdfja latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." .PHONY: text text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." .PHONY: man man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." .PHONY: texinfo texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." .PHONY: info info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." .PHONY: gettext gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." .PHONY: changes changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." .PHONY: linkcheck linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." .PHONY: doctest doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." .PHONY: coverage coverage: $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage @echo "Testing of coverage in the sources finished, look at the " \ "results in $(BUILDDIR)/coverage/python.txt." .PHONY: xml xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." .PHONY: pseudoxml pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." ================================================ FILE: doc/make.bat ================================================ @ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source set I18NSPHINXOPTS=%SPHINXOPTS% source if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled echo. coverage to run coverage check of the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) REM Check if sphinx-build is available and fallback to Python version if any %SPHINXBUILD% 1>NUL 2>NUL if errorlevel 9009 goto sphinx_python goto sphinx_ok :sphinx_python set SPHINXBUILD=call python3 -m sphinx.__init__ %SPHINXBUILD% 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.http://sphinx-doc.org/ exit /b 1 ) :sphinx_ok if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\testy_sphinxy.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\testy_sphinxy.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %~dp0 echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %~dp0 echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "coverage" ( %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage if errorlevel 1 exit /b 1 echo. echo.Testing of coverage in the sources finished, look at the ^ results in %BUILDDIR%/coverage/python.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end ================================================ FILE: doc/repl/.gitignore ================================================ build ================================================ FILE: doc/repl/CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.5) project(sqjsrepl VERSION 4.0 LANGUAGES C CXX) option(ENABLE_VAR_TRACE "Enable variable change tracing feature." OFF) add_subdirectory(../../squirrel "${PROJECT_BINARY_DIR}/squirrel") add_subdirectory(../../squirrel/compiler "${PROJECT_BINARY_DIR}/quirrel-compiler") add_subdirectory(../../sqstdlib "${PROJECT_BINARY_DIR}/sqstdlib") add_subdirectory(../../sqmodules "${PROJECT_BINARY_DIR}/sqmodules") #add_library(sqjsrepl native.cpp) add_executable(sqjsrepl native.cpp) target_link_libraries(sqjsrepl squirrel sqstdlib sqmodules quirrel-compiler) target_include_directories(squirrel PUBLIC "$" "$" "$" "$" ) target_include_directories(quirrel-compiler PUBLIC "$" "$" "$" "$" ) target_include_directories(sqstdlib PUBLIC "$" "$" "$" "$" ) target_include_directories(sqmodules PUBLIC "$" "$" ) target_include_directories(sqjsrepl PUBLIC "$" "$" ) set_target_properties(sqjsrepl PROPERTIES LINK_FLAGS "--bind -sFILESYSTEM=0 -sNO_DISABLE_EXCEPTION_CATCHING") set_target_properties(sqjsrepl PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/static" ) ================================================ FILE: doc/repl/build.txt ================================================ Build instructions: * Install Emscripten SDK * mkdir build * cd build * emcmake cmake .. * emmake make ================================================ FILE: doc/repl/native.cpp ================================================ #include "../../include/squirrel.h" #include "../../include/sqstdaux.h" #include "../../sqmodules/sqmodules.h" #include #include #include // Not using file system static struct WasmSqModulesFileAccess : public ISqModulesFileAccess { virtual void destroy() override {} virtual void getSearchTargets(const char * /*fn*/, bool &search_native, bool &search_script) override { search_native = true; search_script = false; // No file system in Wasm build } virtual void resolveFileName(const char *requested_fn, const char *running_script, string &res) override { // No relative paths because there's no file systems res = requested_fn; } virtual bool readFile(const string &resolved_fn, const char *requested_fn, vector &buf, string &out_err_msg) override { // Should not get here, but just in case out_err_msg = std::string("Cannot read '") + resolved_fn.c_str() + "' module from file"; return false; } } wasm_file_access; static std::string output; static const std::string vlformat(const char * const fmt, va_list va) { va_list vaCopy; va_copy(vaCopy, va); const int iLen = std::vsnprintf(nullptr, 0, fmt, vaCopy); va_end(vaCopy); std::vector zc(iLen + 1); std::vsnprintf(zc.data(), zc.size(), fmt, va); return std::string(zc.data(), iLen); } static const std::string vaformat(const char * const fmt, ...) { va_list vl; va_start(vl, fmt); std::string res = vlformat(fmt, vl); va_end(vl); return res; } static void script_print_func(HSQUIRRELVM /*v*/, const char* s,...) { va_list vl; va_start(vl, s); output += vlformat(s, vl); va_end(vl); } static void script_err_print_func(HSQUIRRELVM /*v*/, const char* s,...) { va_list vl; va_start(vl, s); output += vlformat(s, vl); va_end(vl); } static void compile_error_handler(HSQUIRRELVM /*v*/, SQMessageSeverity sev, const char *desc, const char *source, SQInteger line, SQInteger column, const char *extra_info) { const char *sevName = "error"; if (sev == SEV_HINT) sevName = "hint"; else if (sev == SEV_WARNING) sevName = "warning"; output += vaformat("Squirrel compile %s: %s (%d:%d): %s", sevName, source, int(line), int(column), desc); if (extra_info) { output += "\n"; output += extra_info; } } static SQInteger runtime_error_handler(HSQUIRRELVM v) { assert(sq_gettop(v) == 2); const char *errMsg = nullptr; if (SQ_FAILED(sq_getstring(v, 2, &errMsg))) errMsg = "Unknown error"; output.append(errMsg); if (SQ_SUCCEEDED(sqstd_formatcallstackstring(v))) { const char* callstack = nullptr; sq_getstring(v, -1, &callstack); output.append("\n"); output.append(callstack); sq_pop(v, 1); } else output += "No stack"; return 0; } static std::string runScript(const std::string &source_text) { output.clear(); HSQUIRRELVM vm = sq_open(1024); assert(vm); std::unique_ptr moduleMgr; moduleMgr.reset(new SqModules(vm, &wasm_file_access)); sq_setprintfunc(vm, script_print_func, script_err_print_func); sq_setcompilererrorhandler(vm, compile_error_handler); sq_newclosure(vm, runtime_error_handler, 0); sq_seterrorhandler(vm); sq_enablevartrace(vm, false); moduleMgr->registerMathLib(); moduleMgr->registerStringLib(); moduleMgr->registerSystemLib(); moduleMgr->registerDebugLib(); moduleMgr->registerIoStreamLib(); //moduleMgr->registerIoLib(); // not needed in browser version moduleMgr->registerDateTimeLib(); sq_newtable(vm); HSQOBJECT hBindings; sq_getstackobj(vm, -1, &hBindings); moduleMgr->bindBaseLib(hBindings); moduleMgr->bindRequireApi(hBindings); if (SQ_SUCCEEDED(sq_compile(vm, source_text.c_str(), source_text.length(), "console", true, &hBindings))) { sq_pushnull(vm); //environment if (SQ_FAILED(sq_call(vm, 1, SQTrue, SQTrue))) output += "\nScript execution failed"; else { if (sq_gettype(vm, -1) != OT_NULL) { if (SQ_SUCCEEDED(sq_tostring(vm, -1))) { const char *resStr = nullptr; sq_getstring(vm, -1, &resStr); output += "\n"; output += resStr; sq_pop(vm, 1); } } } sq_pop(vm, 1); // script closure } sq_pop(vm, 1); // bindings table std::string result = output; output.clear(); moduleMgr.reset(); sq_close(vm); return result; } EMSCRIPTEN_BINDINGS(native) { emscripten::function("runScript", &runScript); } ================================================ FILE: doc/repl/static/repl-rst.css ================================================ #repl-root { display: none; /* shown by script after initialization */ } .editor-container { flex: 1; width: 100%; display: flex; flex-direction: column; margin: 8px; } .container-source { height:32rem; } .container-output { height:16rem; } .editor-container:first-child { margin-right: 0; } .editor-header { margin: 0; padding: 0; display: flex; list-style: none; } .editor-header > li { list-style: none; } .editor-header > li:last-child { margin-left: auto; } .editor-header > li > span { height: 38px; line-height: 38px; } .editor-header > li > a { height: 38px; line-height: 38px; padding: .3em .5em; border: 1px solid red; } .editor-area { flex: 1; border: 1px solid lightgray; } .editor-info { margin-top: 6px; height: 160px; border: 1px solid lightgray; padding: 8px; overflow-y: auto; } .editor-info li { cursor: pointer; } .editor-sub-header { padding: 4px 8px; } .output { border: 1px solid lightgray; padding: 8px; overflow-y: auto; flex: 1; } button#btn-run { margin-left:auto; } ================================================ FILE: doc/repl/static/repl.css ================================================ #repl-root { display: none; /* shown by script after initialization */ height: 100vh; } .editor-container { flex: 1; width: 100%; display: flex; flex-direction: column; margin: 8px; } .editor-container:first-child { margin-right: 0; } .editor-header { margin: 0; padding: 0; display: flex; list-style: none; } .editor-header > li { list-style: none; } .editor-header > li:last-child { margin-left: auto; } .editor-header > li > span { height: 38px; line-height: 38px; } .editor-header > li > a { height: 38px; line-height: 38px; padding: .3em .5em; border: 1px solid red; } .editor-area { flex: 1; border: 1px solid lightgray; } .editor-info { margin-top: 6px; height: 160px; border: 1px solid lightgray; padding: 8px; overflow-y: auto; } .editor-info li { cursor: pointer; } .editor-sub-header { padding: 4px 8px; } .output { border: 1px solid lightgray; padding: 8px; overflow-y: auto; flex: 1; } button#btn-run { padding: 0.25rem 1rem; } ================================================ FILE: doc/repl/static/repl.html ================================================ Quirrel REPL
  • Source code

  
  • Result

  
================================================ FILE: doc/repl/static/replmain.js ================================================ // Setup editor const sourceEditor = ace.edit("source-editor") sourceEditor.setShowPrintMargin(false) sourceEditor.setValue(localStorage.getItem('sourceCode') || 'println("Hello, Quirrel!")') sourceEditor.moveCursorTo(0, 0) function execute() { const sourceCode = sourceEditor.getValue() localStorage.setItem('sourceCode', sourceCode) const result = Module.runScript(sourceCode) $('#output').text(result) } $('#btn-run').on('click', execute) document.addEventListener('keyup', (e)=>{ if (e.key === 'F9') execute() }, false) // Show page $('#repl-root').css({'display': 'flex'}) // WebAssembly var Module = { onRuntimeInitialized: function() { // Initialize here } } ================================================ FILE: doc/requirements.txt ================================================ Sphinx==8.2.3 myst-parser==4.0.1 sphinx-rtd-theme==3.0.2 ================================================ FILE: doc/source/_static/custom.css ================================================ .toggle .header { display: block; clear: both; } .toggle .header:after { content: " ▶"; } .toggle .header.open:after { content: " ▼"; } /* Bar charts on the performance page.*/ /* Licensed with MIT license from https://github.com/wren-lang/wren */ table.chart { margin: 4px 0 0 0; padding: 0 0 0 25px; } table.chart td, th { line-height: 15px; margin: 0; padding: 1px 0; } table.chart th { font-size: 14px; width: 200px; } table.chart .chart-bar { display: inline-block; padding: 2px 0 0 0; color: #eeeeee; background: hsl(200, 60%, 50%); border-bottom: solid 1px hsl(210, 60%, 20%); text-align: right; border-radius: 2px; } table.chart .chart-bar.featured { background: hsl(200, 60%, 25%); border-bottom: solid 1px hsl(210, 60%, 20%); } /* End of wren style */ ================================================ FILE: doc/source/_templates/page.html ================================================ {% extends "!page.html" %} {% block footer %} {% endblock %} ================================================ FILE: doc/source/conf.py ================================================ # -*- coding: utf-8 -*- # # Quirrel documentation build configuration file, created by # sphinx-quickstart on Sun Jan 31 00:26:52 2016. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. import sys import os import time import re # 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. #sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. #needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. sys.path.insert(0, os.path.abspath('.')) extensions = [ 'quirrel', 'myst_parser', 'quirrel_pygment_lexer' ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: source_suffix = ['.rst', '.md'] # The encoding of source files. #source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = u'Quirrel documentation' copyright = '2003-2016, Alberto Demichelis; 2016-%s, Gaijin Entertainment' % time.strftime('%Y') author = u'Alberto Demichelis; Vasiliy Ryabtsev; Gaijin Entertainment' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # def read_version_from_source(): """Extract version from squirrel.h""" header_path = os.path.join(os.path.dirname(__file__), '..', '..', 'include', 'squirrel.h') header_path = os.path.abspath(header_path) with open(header_path, 'r') as f: content = f.read() major = re.search(r'#define\s+SQUIRREL_VERSION_NUMBER_MAJOR\s+(\d+)', content).group(1) minor = re.search(r'#define\s+SQUIRREL_VERSION_NUMBER_MINOR\s+(\d+)', content).group(1) patch = re.search(r'#define\s+SQUIRREL_VERSION_NUMBER_PATCH\s+(\d+)', content).group(1) return f'{major}.{minor}.{patch}' version = read_version_from_source() # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = 'en' # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: #today = '' # Else, today_fmt is used as the format for a strftime call. #today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. exclude_patterns = [] # The reST default role (used for this markup: `text`) to use for all # documents. #default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. #add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). #add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. #show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'sphinx' # A list of ignored prefixes for module index sorting. #modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. #keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- 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 = 'sphinx_rtd_theme' # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. #html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. #html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". #html_title = None # A shorter title for the navigation bar. Default is the same as html_title. #html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. html_logo = 'simple_nut.png' # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. html_favicon = 'nut.ico' # 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', '../repl/static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. #html_extra_path = [] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. #html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. #html_use_smartypants = True # Custom sidebar templates, maps document names to template names. #html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. #html_additional_pages = {} # If false, no module index is generated. #html_domain_indices = True # If false, no index is generated. #html_use_index = True # If true, the index is split into individual pages for each letter. #html_split_index = False # If true, links to the reST sources are added to the pages. #html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. #html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. #html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. #html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). #html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'hu', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'ru', 'sv', 'tr' #html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # Now only 'ja' uses this config value #html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. #html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. htmlhelp_basename = 'squirrel_doc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). #'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). #'pointsize': '10pt', # Additional stuff for the LaTeX preamble. #'preamble': '', # Latex figure (float) alignment #'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). _stdauthor = r'Alberto Demichelis; Vasiliy Ryabtsev; Gaijin Entertainment' latex_documents = [ ('reference/index', 'reference.tex', 'Quirrel Reference Manual', _stdauthor, 'manual'), ('stdlib/index', 'stdlib.tex', 'Quirrel Standard Library', _stdauthor, 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. #latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. #latex_use_parts = False # If true, show page references after internal links. #latex_show_pagerefs = False # If true, show URL addresses after external links. #latex_show_urls = False # Documents to append as an appendix to all manuals. #latex_appendices = [] # If false, no module index is generated. #latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'Quirrel', u'Quirrel Documentation', [author], 1) ] # If true, show URL addresses after external links. #man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'Quirrel', u'Quirrel Documentation', author, 'Quirrel', 'The Programming Language.', 'Miscellaneous'), ] def setup(app): app.add_css_file('custom.css') # Documents to append as an appendix to all manuals. #texinfo_appendices = [] # If false, no module index is generated. #texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. #texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. #texinfo_no_detailmenu = False ================================================ FILE: doc/source/diff_from_squirrel.rst ================================================ .. _diff_from_squirrel: ******************************************* Differences from Squirrel language ******************************************* .. index:: single: diff_from_squirrel Squirrel language was used a lot in all Gaijin projects since 2006. Gaijin used Lua before but found Lua too unsafe and hard to debug, and also hard sometimes to bind with native code due to lack of types. It is well known and there are lot info on this in the Internet. Squirrel has not only C++ like syntax (which is helpful for C++ programmers sometimes), but also has type system much closer to C/C++ code. It also has safer syntax and semantic. However during heavy usage we found that some features of language are not used in our real codebase of millions of LoC in 20+ projects on 10+ platforms. Some language features were also not very safe, or inconsistent. We needed stricter language with tools for easier refactor and support. And of course in game-development any extra performance is always welcome. Basically in any doubt we used 'Zen of Python'. We renamed language from Squirrel to Quirrel to avoid ambiguity. ------------ New features ------------ Language syntax =============== * Template strings: ``$"Hello {name}"`` for string interpolation * Documentation strings: ``@@"docstring"`` for inline documentation * Compiler directives: ``#pragma`` for configuring compiler options * Type method access: ``obj.$method`` and ``obj?.$method`` * Numeric literal separators: ``1_000_000`` for better readability * Supports destructuring assignment to unpack values from arrays and objects (e.g., ``let {a, b} = table``), including destructuring in function parameters with default values and type annotations * ES2015-style shorthand table initialization: ``{x, y}`` instead of ``{x=x, y=y}`` * Named functions in expressions: ``let foo = function foo() {}`` * Null-safety operators: * ``?.`` - Null-conditional member access: ``obj?.property`` * ``?[`` - Null-conditional indexing: ``arr?[index]`` * ``?(`` - Null-conditional function call: ``func?(args)`` * ``??`` - Null-coalescing operator: ``value ?? default`` * New keywords: * ``let`` - Immutable binding declaration (alternative to ``local``) * ``global`` - Explicit global variable declaration * ``not in`` - For membership testing, alternative to ``!(x in y)`` * ``static`` - For evaluating expressions once on first access and memoizing the result * ``const`` can also be used for inline declaration inside expressions Static analysis =============== * Comprehensive static analyzer 90+ diagnostic checks (including reporting unused variables, functions, imports, parameters, unreachable code detection, type mismatches and null safety violations, data flow analysis, scope and visibility checks and more) Compiler architecture ===================== * AST-based multi-pass compilation (vs. Squirrel's single-pass direct compilation) VM architecture ==================== * Class method hinting for performance optimization (approaching LuaJIT non-JIT performance) * Added tracking of changing values of container fields (saving the stack of code that modifies the values) Type system & immutability =========================== * Immutability support: * ``freeze()`` function for protecting references to instances, tables, arrays from modification * ``let`` keyword for non-assignable bindings * Pure function marking for optimization hints and use in constants Standard library ================ * Base library additions: * ``println()``, ``errorln()`` - print/error with newline variants * ``print()``, ``println()``, ``error()``, ``errorln()`` accept arbitrary number of parameters to print * ``freeze(obj)`` - return an immutable reference to an object (also ``getobjflags(obj)`` to get object flags (e.g., SQOBJ_FLAG_IMMUTABLE) and ``is_frozen()`` for container types. * ``deduplicate_object(obj)`` - deduplicate table/array contents for memory efficiency * Enhanced ``assert()`` - accepts closures for lazy message evaluation * Support for ``call()``/``acall()`` type methods for classes * Support negative indexes in ``array.slice()`` and ``string.slice()`` * String type methods: * Added: ``.hash()``, ``.contains()``, ``.hasindex()``, ``.subst()``, ``.replace()``, ``.join()``, ``.concat()``, ``.clone()``, plus moved from stdlib: ``.strip()``, ``.lstrip()``, ``.rstrip()``, ``.split()``, ``.split_by_chars()``, ``.escape()``, ``.startswith()``, ``.endswith()`` (some are shared with the string stardard library) * Changed: ``.find()`` renamed to ``.indexof()`` to reduce ambiguity * Table type methods: * Added:** ``.each()``, ``.reduce()``, ``.findindex()``, ``.findvalue()``, ``.topairs()``, ``.swap()``, ``.__merge()``, ``.__update()``, ``.replace_with()``, ``.hasindex()``, ``.hasvalue()``, ``.clone()``, ``.is_frozen()``, ``.getfuncinfos()`` * Removed: ``.setdelegate()``, ``.getdelegate()`` * Array type methods: * Added: ``.each()``, ``.findindex()``, ``.findvalue()``, ``.contains()``, ``.hasindex()``, ``.hasvalue()``, ``.totable()``, ``.replace_with()``, ``.swap()``, ``.clone()``, ``.is_frozen()`` * Changed: * ``.find()`` renamed to ``.indexof()`` * ``.append()`` and ``.extend()`` now accept multiple arguments * Removed: ``.push()`` (use ``.append()`` instead) * Standard library modules: * Added: datetime, debug, and serialization modules * String module: Functions moved to string type methods for convenience (still available as module functions) Module system ============= Support for modules. Two APIs - runtime (``require()`` function) and compile-time (``import`` statement) ---------------- Removed features ---------------- Language features ================= * Octal number literals - was error-prone, ``0123`` syntax now produces an error * Comma operator - removed from expressions (was never used, but error-prone) * Class and member attributes - ```` XML-style attribute syntax removed (were never used in real code) * ``#`` single line comments - only ``//`` comments supported now (consistency - "one obvious way") * ``rawcall`` keyword * Post-initializer syntax - removed due to ambiguity (especially with optional commas) * ``class::method`` syntax sugar - adding table/class methods this way disallowed (as There should be one -- and preferably only one -- obvious way to do it). Declare methods in-place or explicitly add slots with ``<-`` operator. Base library functions ====================== The following functions were removed for security, simplicity, or architectural reasons: ``setroottable()``, ``setconsttable()``, ``seterrorhandler()`` ---------------- Changes ---------------- Scoping and declarations ========================= * Functions and classes are locally scoped * Constants and enums are locally scoped by default - use ``global`` keyword for global declarations * ``global`` keyword required for declaring global constants and enums explicitly * Referencing the closure environment object (``this``) is explicit - no implicit ``this`` lookup * Usage of the root table is deprecated * No fallback to global root table for undeclared variables - must use ``::`` operator to access globals (if root table is enabled at all) Compilation Model ================= * AST-based compiler (vs. Squirrel's single-pass direct bytecode generation): * Multi-pass compilation with intermediate AST representation * AST optimizations (closure hoisting, constant folding) * Static analyzer operates on AST C/C++ Binding API ================= * Human-readable function type declarations: * Replaces cryptic type masks (``"ac."``) with readable syntax * Example: ``split_by_chars(str: string, separators: string, [skip_empty: bool]): array`` * Non-stack-based C APIs for better performance Class declaration syntax ======================== * Python-style class inheritance: ``class Baz(Bar)`` instead of ``class Baz extends Bar`` * Class ``extends`` keyword removed in favor of parentheses-based syntax Operators ========= * ``delete`` is deprecated: * Now configurable via pragmas: ``#forbid-delete-operator`` / ``#allow-delete-operator`` * Recommended to use ``table.$rawdelete()`` or ``table.rawdelete()`` instead Type methods ================= * ``.find()`` renamed to ``.indexof()`` for arrays and strings * ``array.append()`` and ``array.extend()`` now accept multiple arguments * ``array.filter()`` argument order changed to match map/reduce conventions (and in certain degree the one of ``foreach`` by making key argument optional) * ``array.filter()``, ``array.map()`` and ``array.reduce()`` callbacks optionally accept the reference to the container being processed (instead of passing it via ``this``) * ``getinfos()`` renamed to ``getfuncinfos()`` and now works on callable objects (instances, tables) Error handling ============== * Structured diagnostics with severity levels * Error callback with source location, diagnostic ID, and extra information * Configurable static analyzer warnings -------------------------------- Possible future changes -------------------------------- See RFCs page ================================================ FILE: doc/source/index.rst ================================================ .. Quirrel documentation master file, created by sphinx-quickstart on Sun Jan 31 00:26:52 2016. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Quirrel documentation ========================================= .. toctree:: :maxdepth: 1 introduction.rst diff_from_squirrel.rst reference/index.rst stdlib/index.rst modules/index.rst modules/bindings.rst repl/index.rst rfcs/STATUS.md rfcs/README.md Indices and tables ================== * :ref:`genindex` * :ref:`search` ================================================ FILE: doc/source/introduction.rst ================================================ .. _introduction: ************ Introduction ************ .. index:: single: introduction Quirrel is a fast, high-level imperative, OO programming language, designed to be a lightweight but powerful scripting tool that fits within the size, memory bandwidth, and real-time requirements of applications like games. Quirrel has simple integration and bindings in any C++ application. Quirrel has C/Java/C++ like syntax with clear, rich and simple type system, but the language has a very dynamic nature like Python/Lua/JavaScript. Easy to start while being much safer than Lua and JS, having the equal performance with Lua. It is based on Squirrel Script language (http://www.squirrel-lang.org/), but modified to be safer, stricter and faster. Quirrel is not compatible with the original Squirrel (see :ref:`Diff from Squirrel `), but conversion usually can be done easily. Quirrel offers a wide range of features like dynamic typing, higher order functions, generators, tail recursion, exception handling, coroutines, automatic memory management. Repository is available on https://github.com/GaijinEntertainment/quirrel and your PR's and feedback are welcome! .. _more_details_on_quirrel: ---------------------------- More details on Quirrel ---------------------------- .. index:: single: more details on quirrel - Quirrel is a mature scripting language used in tens of games and other apps (including web servers and embeddable platforms) with hundreds of millions installations. It was created to be embeddable language to C/C++ applications with easy bindings and clear mental model. - Quirrel is familiar and convenient for those who have experience with C, C++, JavaScript or Python. It has a small and simple but sufficient :ref:`types system `: float and integer types (not just 'numeric'), built-in tables, classes and arrays, functions, closures, generators and coroutines (as well as null, string and boolean types). In most cases it is completely obvious how to bind native code, class or function, how to call native code and work with native types. And of course, arrays indices start with 0. To be obvious for C++ programmer in both programming and binding is one of the main ideas Quirrel Language Python and JavaScript are not lightweight nor easily embeddable; JS has also an infamous type system with all its implicitness. Lua on the other hand is as small as Quirrel, but because of many differences from C/C++ it often confuses even experienced programmers. - Quirrel has a robust and predictable :ref:`memory model `. In most cases with reference count you never have unexpected freezes in runtime as well as predictable memory consumption. However, we are planning to add GC memory management without reference count, as it can be more beneficial in some ways of using language (when you always have enough computational time in separate thread to manage memory it allows you to have less latency) - Quirrel supports concurrency with :ref:`coroutines `. - Quirrel wants to be safe. The main intent to evolve Squirrel into Quirrel was to make it safer. Being a dynamic language by its nature doesn't mean that you can be more safe. Safety comes from different small syntactic and semantic changes, allowing more compile time validations and explicitly, making code less ambiguous and clear to reader and writer. Things like 'let' :ref:`Named bindings declaration `, :ref:`freezing ` of tables and arrays, :ref:`Destructuring assignment ` and imports, deprecation of modifying classes, redefine table keys and several others, as well as implementing static analyzer, helps to prevent many errors and allow code to be stricter. - Quirrel is fast. It is at least as fast as Lua (on compilation and runtime), with some tweaks to be sometimes closer to LuaJit with no JIT. We are also going to implement AST-based compiler with optimizations on AST, allowing code to become even faster in real-time (that will also allow making code safer and to add power of macros). Class hinting allows classes to be as fast as in `Wren `_ or LuaJIT. ================================================ FILE: doc/source/modules/bindings.rst ================================================ Quirrel bindings ----------------------------------- Guide in bindings ================= Not full guide but some brief introduction. To get deeper into bindings you need to study samples or read the code of SqRat Sqrat is a C++ binding library designed for the Quirrel scripting language. Sqrat simplifies the process of exposing C++ functions and classes to Quirrel scripts. It automates type conversion between C++ and Quirrel, making it easier to register functions, methods, and classes without needing to manually handle the Quirrel stack in every case. Example ^^^^^^^^^^^^^^^^^^ Basic Includes ^^^^^^^^^^^^^^^^^^ .. code-block:: cpp #include #include Create a module ^^^^^^^^^^^^^^^^^^^^^ SqModules allows to export Quirrel objects (usually tables and classes) as script modules. Example of module creation: :: Sqrat::Table exports(vm); // Create a table to hold the exported functions and classes exports .Func("function_name", function) // Export a C++ function with automatic handling of arguments and return values .SquirrelFuncDeclString(add, "add(a: int, b: int): int", "Returns the sum of two integers, optional string")// Export a C++ function that works with the Quirrel stack, fully documented .SquirrelFunc("function_name", low_level_function) // Export a C++ function that works with the Quirrel stack .SetValue("SOME_ID", value) // Exports is a table, so it can hold any Quirrel value ; module_manager->addNativeModule("module_name", exports); // Expose the table as a module to the Quirrel scripts Simple bind of cpp function ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ You can use Sqrat::Table::Func() or Sqrat::Class::Func() to register a C++ function for use in a Quirrel script. Sqrat automatically handles argument and return value conversions between Quirrel and C++ types. C++: :: Sqrat::Table exports(vm); exports.Func("pow", &std::pow); // Register pow function in the script module_manager->addNativeModule("math_demo", exports); Usage in Quirrel: :: from "math_demo" import pow // or let { pow } = require("math_demo") local result = pow(2, 3) // Calls the C++ std::pow(2, 3) print(result) // Output: 8 Bind function that works with Quirrel stack ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ For more control, you can manually handle the Quirrel stack by using SquirrelFunc. This allows you to specify custom logic for processing function arguments and returning values. For instance, you may need this for variadic functions, arguments of varying types, or functions with complex logic that can't be automatically bound. :: SQInteger sum_all_args(HSQUIRRELVM v) { SQInteger n = sq_gettop(v); // Number of arguments SQFloat sum = 0; for (SQInteger i = 2; i <= n; ++i) { // Sum all arguments, starting from 2 (#1 is `this`) SQFloat val; if (SQ_SUCCEEDED(sq_getfloat(v, i, &val))) // For demo purposes, we only handle numbers and ignore other types sum += val; } sq_pushfloat(v, sum); // Push the result return 1; // Number of return values (0 for void, 1 for return value, SQ_ERROR for error) } exports.SquirrelFunc("sum_all_args", sum_all_args, -2); // -2: variable arguments, at least 2 (`this` and a number In script: :: local result = sum_all_args(1, 2, 3, 4) // Calls the C++ function print(result) // Output: 10 Full signature of SquirrelFunc: .. cpp:function:: TableBase& Table::SquirrelFunc(const char* name, SQFUNCTION func, SQInteger nparamscheck, const char *typemask=nullptr, const char *docstring=nullptr, SQInteger nfreevars=0, const Object *freevars=nullptr) :param name: should be string, func should be function that works with Quirrel :param nparamscheck: number of arguments of function. If negative - function can have at least this number of arguments but can accept more. :param typemask: optional typemask (see sq_setparamscheck in :ref:`API `) :param docstring: optional docstring, nfreevars and freevars - free variables of function. :param nfreevars: number of free variables :param freevars: free variables to capture :remarks: The typemask is a string that represent the expected parameter type. The types are expressed as follows: 'o' null, 'i' integer, 'f' float, 'n' integer or float, 's' string, 't' table, 'a' array, 'u' userdata, 'c' closure and nativeclosure, 'g' generator, 'p' userpointer, 'v' thread, 'x' instance(class instance), 'y' class, 'b' bool. and '.' any type. The symbol '|' can be used as 'or' to accept multiple types on the same parameter. There isn't any limit on the number of 'or' that can be used. Spaces are ignored so can be inserted between types to increase readability. For instance to check a function that expect a table as 'this' a string as first parameter and a number or a userpointer as second parameter, the string would be "tsn|p" (table,string,number or userpointer). If the parameters mask is contains fewer parameters than 'nparamscheck', the remaining parameters will not be typechecked. The preferred method for binding C++ functions that work with the Quirrel stack is to use `.SquirrelFuncDeclString`. This declarative method allows you to specify the function signature, argument types (including optional/default values), return type, and documentation string all in one place. **Example:** .. code-block:: cpp .SquirrelFuncDeclString( do_math, "pure do_math(a: int, [b: number = 2]): float", "Performs math operation. Optional argument 'b' defaults to 2." ) This approach allows the engine to: - Parse argument types and generate typemasks for type checking - Reflect metadata for documentation and scripting tools - Recognize pure functions for optimization **Why use SquirrelFuncDeclString?** - **Declarative:** Full signature, types, defaults, and docstring in a single place. - **Robust:** More accurate binding and automatic validation. - **Documentation-friendly:** Signature and docs are automatically extracted. **Deprecation Notice:** The older `.SquirrelFunc` macro is now **deprecated**. It required manually handling arguments from the Quirrel VM stack, which is more error-prone and lacks reflection support. **Always prefer `.SquirrelFuncDeclString` for new bindings.** Bind classes, constants and values ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Sqrat allows to register C++ classes with member variables and methods that can be accessed from Quirrel scripts. Toy example: :: class Rect { public: float width, height; Rect(float w, float h) : width(w), height(h) {} float area() const { return width * height; } float perimeter() const { return 2 * (width + height); } }; Sqrat::Class rectClass(table.GetVM(), "Rect"); rectClass .Ctor() .Var("width", &Rect::width) .Var("height", &Rect::height) .Func("area", &Rect::area) .Prop("perimeter", &Rect::perimeter) ; exports.Bind("Rect", rectClass); // Bind the class to the table module_manager->addNativeModule("geometry", exports); In script: :: from "geometry" import Rect local r = Rect(1, 3) r.width = 2 print(r.area()) // Output: 6 print(r.perimeter) // Output: 10 SquirrelCtor() may be used for a constructor with a flexible behavior. It has to implement an actual native instance creation. Example: :: static SQInteger rect_ctor(HSQUIRRELVM v) { SQInteger n = sq_gettop(v); if (n == 2) { // copy constructor if (!Sqrat::check_signature(vm, 2)) return sq_throwerror(vm, "Invalid type passed to copy ctor"); } else if (n != 1 && n != 3) return sqstd_throwerrorf(vm, "Invalid arguments count %d", n); Rect *instance = new Rect(0, 0); if (n == 1) // no arguments ; // already initialized with default 0, 0 else if (n == 3) { // instance and 2 numbers SQFloat w, h; sq_getfloat(v, 2, &w); // Should always succeed, because types are specified in type mask (see SquirrelCtor) sq_getfloat(v, 3, &h); instance->width = w; instance->height = h; } else if (n == 2) { // copy constructor (self instance and another instance) Rect *other = Sqrat::Var(vm, 2).value; instance->width = other->width; instance->height = other->height; } Sqrat::ClassType::SetManagedInstance(vm, 1, instance); // Link with script instance return 1; } Sqrat::Class rectClass(table.GetVM(), "Rect"); rectClass .SquirrelCtor(rect_ctor, 0, "x y|n n") // 0 - no argument count check, "x y|n n" - type mask (instance('this') and instance or 2 numbers) .Var("width", &Rect::width) .Var("height", &Rect::height) .Func("area", &Rect::area) .Prop("perimeter", &Rect::perimeter) ; In script: :: from "geometry" import Rect local r1 = Rect(1, 3) local r2 = Rect(r1) local r3 = Rect() Consttable - not documented Class property (setter) - not documented Static functions - not documented ================================================ FILE: doc/source/modules/index.rst ================================================ .. _index: ************ Modules ************ Project source code is available at https://github.com/GaijinEntertainment/quirrel Quirrel provides modules functionality via separate library. Modules are useful to encapsulate code (only explicitly exported data/methods become available outside), to make the code reusable and to prevent contaminating global namespace (root table). The provided reference implementation also allows reloading of script code while keeping data intact. ================== Script API ================== .. index:: single: Script API Module code is executed with a special table as context ('this'). It has the following functions bound: .. sq:function:: require(filename) If given module was not loaded yet, reads the file, executes it and returns module exports. Module exports are data returned by module body code. If module was loaded before, just returns its exports. If file was not found, throws an error. .. sq:function:: require_optional(filename) The same as require(), but for missing file it returns null instead of throwing an error. .. sq:function:: persist(key, initializer) Used to keep data between sequential execution of the same file for hot reloading. For the first run of the module calls provided initializer function and returns the result. Also it stores a reference to the result in a special table that is later reused when the file with the same name is executed. The 'key' parameter is the unique-per-module identifier of the variable. Example: :: let counter = persist("counter", @() {value=0}) .. sq:function:: keepref(var) An utility function that keeps a reference to the given object. Might be useful to keep an object from deleting itself if it is not exported outside the module. Also module 'this' table contains field __name__ which is set to file name for module loaded by script require() function or parameter __name__ passed to C++ requireModule() function (defaults to "__main__" for entry point script). .. quirrel.native_modules Module that allow to list all registered native modules. Example: :: require("quirrel.native_modules").each(println) Will print all currently registered native modules. ================== Native API ================== .. index:: single: Native API Module functionality is implemented in SqModules C++ class with the following public methods: .. cpp:function:: SqModules::SqModules(HSQUIRRELVM vm) Constructs a module manager instance for the given Quirrel VM. .. cpp:function:: HSQUIRRELVM SqModules::getVM() Returns module manager VM. .. cpp:function:: bool SqModules::requireModule(const char *fn, bool must_exist, const char *__name__, SqObjPtr &exports, std::string &out_err_msg) :param const char \*fn: name of the file to load :param bool must_exist: if set to false treat missing file as success (return true) :param const char \*__name__: value of module's __name__ field (set nullptr to use actual file name) :param SqObjPtr &exports: object to store export to :param std\:\:string &out_err_msg: receives error message if call failed :returns: true if succeeded :remarks: Actually this is a function bound to script as require() Loads and executes script module or just returns its exports if it was already run. .. cpp:function:: bool SqModules::reloadModule(const char *fn, bool must_exist, const char *__name__, SqObjPtr &exports, std::string &out_err_msg) Basically the same as requireModule, but it unloads all previously loaded modules and newly executes all modules pulled by require() calls. This can also be used for initial module execution - so that the first call to reloadModule(entry_point_fn) will load all modules and initialize persistent data and subsequent calls to reloadModule(entry_point_fn) will reload the module and all it depends on while reusing kept persistent data. .. cpp:function:: bool SqModules::addNativeModule(const char *module_name, const SqObjPtr &exports) Registers a Quirrel object 'exports' under the provided 'module_name', so that it can be require()-d in script. .. cpp:function:: void SqModules::registerMathLib() Registers standard "math" native library .. cpp:function:: void SqModules::registerStringLib() Registers standard "string" native library .. cpp:function:: void SqModules::registerIoStreamLib() Registers standard "iostream" native library module (blob and stream classes) .. cpp:function:: void SqModules::registerDateTimeLib() Registers standard "datetime" native library .. cpp:function:: void SqModules::registerDebugLib() Registers "debug" native library .. cpp:function:: void SqModules::registerSystemLib() Registers standard 'system' library as "system" native module .. cpp:function:: void SqModules::registerIoLib() Registers standard 'io' library as "io" native module ================================================ FILE: doc/source/quirrel.py ================================================ # -*- coding: utf-8 -*- """ quirrel ~~~~~~~~~~~~~~~~~~~~~~~~~ The Quirrel domain. :copyright: Copyright 2007-2019 by the Sphinx team, see AUTHORS. :license: BSD, see LICENSE for details. NOTE: this is just modified copypaste of javascript from sphinx """ from docutils import nodes from docutils.parsers.rst import directives from sphinx import addnodes from sphinx.directives import ObjectDescription from sphinx.domains import Domain, ObjType from sphinx.locale import _ from sphinx.roles import XRefRole from sphinx.util.docfields import Field, GroupedField, TypedField from sphinx.util.docutils import SphinxDirective from sphinx.util.nodes import make_refnode if False: # For type annotation from typing import Any, Dict, Iterator, List, Tuple # NOQA from docutils import nodes # NOQA from sphinx.application import Sphinx # NOQA from sphinx.builders import Builder # NOQA from sphinx.environment import BuildEnvironment # NOQA class SQObject(ObjectDescription): """ Description of a Quirrel object. """ #: If set to ``True`` this object is callable and a `desc_parameterlist` is #: added has_arguments = False #: what is displayed right before the documentation entry display_prefix = None # type: unicode #: If ``allow_nesting`` is ``True``, the object prefixes will be accumulated #: based on directive nesting allow_nesting = False def handle_signature(self, sig, signode): # type: (unicode, addnodes.desc_signature) -> Tuple[unicode, unicode] """Breaks down construct signatures Parses out prefix and argument list from construct definition. The namespace and class will be determined by the nesting of domain directives. """ sig = sig.strip() if '(' in sig and sig[-1:] == ')': member, arglist = sig.split('(', 1) member = member.strip() arglist = arglist[:-1].strip() else: member = sig arglist = None # If construct is nested, prefix the current prefix prefix = self.env.ref_context.get('sq:object', None) mod_name = self.env.ref_context.get('sq:module') name = member try: member_prefix, member_name = member.rsplit('.', 1) except ValueError: member_name = name member_prefix = '' finally: name = member_name if prefix and member_prefix: prefix = '.'.join([prefix, member_prefix]) elif prefix is None and member_prefix: prefix = member_prefix fullname = name if prefix: fullname = '.'.join([prefix, name]) signode['module'] = mod_name signode['object'] = prefix signode['fullname'] = fullname if self.display_prefix: signode += addnodes.desc_annotation(self.display_prefix, self.display_prefix) if prefix: signode += addnodes.desc_addname(prefix + '.', prefix + '.') elif mod_name: signode += addnodes.desc_addname(mod_name + '.', mod_name + '.') signode += addnodes.desc_name(name, name) if self.has_arguments: paramlist = addnodes.desc_parameterlist() if arglist: paramlist += addnodes.desc_parameter(arglist, arglist) signode += paramlist return fullname, prefix def add_target_and_index(self, name_obj, sig, signode): # type: (Tuple[unicode, unicode], unicode, addnodes.desc_signature) -> None mod_name = self.env.ref_context.get('sq:module') fullname = (mod_name and mod_name + '.' or '') + name_obj[0] if fullname not in self.state.document.ids: signode['names'].append(fullname) signode['ids'].append(fullname.replace('$', '_S_')) signode['first'] = not self.names self.state.document.note_explicit_target(signode) objects = self.env.domaindata['sq']['objects'] if fullname in objects: self.state_machine.reporter.warning( 'duplicate object description of %s, ' % fullname + 'other instance in ' + self.env.doc2path(objects[fullname][0]), line=self.lineno) objects[fullname] = self.env.docname, self.objtype indextext = self.get_index_text(mod_name, name_obj) if indextext: self.indexnode['entries'].append(('single', indextext, fullname.replace('$', '_S_'), '', None)) def get_index_text(self, objectname, name_obj): # type: (unicode, Tuple[unicode, unicode]) -> unicode name, obj = name_obj if self.objtype == 'function': if not obj: return _('%s() (built-in function)') % name return _('%s() (%s method)') % (name, obj) elif self.objtype == 'class': return _('%s() (class)') % name elif self.objtype == 'data': return _('%s (global variable or constant)') % name elif self.objtype == 'attribute': return _('%s (%s attribute)') % (name, obj) return '' def before_content(self): # type: () -> None """Handle object nesting before content :py:class:`SQObject` represents Quirrel language constructs. For constructs that are nestable, this method will build up a stack of the nesting hierarchy so that it can be later de-nested correctly, in :py:meth:`after_content`. For constructs that aren't nestable, the stack is bypassed, and instead only the most recent object is tracked. This object prefix name will be removed with :py:meth:`after_content`. The following keys are used in ``self.env.ref_context``: sq:objects Stores the object prefix history. With each nested element, we add the object prefix to this list. When we exit that object's nesting level, :py:meth:`after_content` is triggered and the prefix is removed from the end of the list. sq:object Current object prefix. This should generally reflect the last element in the prefix history """ prefix = None if self.names: (obj_name, obj_name_prefix) = self.names.pop() prefix = obj_name_prefix.strip('.') if obj_name_prefix else None if self.allow_nesting: prefix = obj_name if prefix: self.env.ref_context['sq:object'] = prefix if self.allow_nesting: objects = self.env.ref_context.setdefault('sq:objects', []) objects.append(prefix) def after_content(self): # type: () -> None """Handle object de-nesting after content If this class is a nestable object, removing the last nested class prefix ends further nesting in the object. If this class is not a nestable object, the list of classes should not be altered as we didn't affect the nesting levels in :py:meth:`before_content`. """ objects = self.env.ref_context.setdefault('sq:objects', []) if self.allow_nesting: try: objects.pop() except IndexError: pass self.env.ref_context['sq:object'] = (objects[-1] if len(objects) > 0 else None) class SQCallable(SQObject): """Description of a Quirrel function, method or constructor.""" has_arguments = True doc_field_types = [ TypedField('arguments', label=_('Arguments'), names=('argument', 'arg', 'parameter', 'param'), typerolename='func', typenames=('paramtype', 'type')), GroupedField('errors', label=_('Throws'), rolename='err', names=('throws', ), can_collapse=True), Field('returnvalue', label=_('Returns'), has_arg=False, names=('returns', 'return')), Field('returntype', label=_('Return type'), has_arg=False, names=('rtype',)), ] class SQConstructor(SQCallable): """Like a callable but with a different prefix.""" display_prefix = 'class ' allow_nesting = True class SQModule(SphinxDirective): """ Directive to mark description of a new Quirrel module. This directive specifies the module name that will be used by objects that follow this directive. Options ------- noindex If the ``noindex`` option is specified, no linkable elements will be created, and the module won't be added to the global module index. This is useful for splitting up the module definition across multiple sections or files. :param mod_name: Module name """ has_content = False required_arguments = 1 optional_arguments = 0 final_argument_whitespace = False option_spec = { 'noindex': directives.flag } def run(self): # type: () -> List[nodes.Node] mod_name = self.arguments[0].strip() self.env.ref_context['sq:module'] = mod_name noindex = 'noindex' in self.options ret = [] if not noindex: self.env.domaindata['sq']['modules'][mod_name] = self.env.docname # Make a duplicate entry in 'objects' to facilitate searching for # the module in QuirrelDomain.find_obj() self.env.domaindata['sq']['objects'][mod_name] = (self.env.docname, 'module') targetnode = nodes.target('', '', ids=['module-' + mod_name], ismod=True) self.state.document.note_explicit_target(targetnode) ret.append(targetnode) indextext = _('%s (module)') % mod_name inode = addnodes.index(entries=[('single', indextext, 'module-' + mod_name, '', None)]) ret.append(inode) return ret class SQXRefRole(XRefRole): def process_link(self, env, refnode, has_explicit_title, title, target): # type: (BuildEnvironment, nodes.Node, bool, unicode, unicode) -> Tuple[unicode, unicode] # NOQA # basically what sphinx.domains.python.PyXRefRole does refnode['sq:object'] = env.ref_context.get('sq:object') refnode['sq:module'] = env.ref_context.get('sq:module') if not has_explicit_title: title = title.lstrip('.') target = target.lstrip('~') if title[0:1] == '~': title = title[1:] dot = title.rfind('.') if dot != -1: title = title[dot + 1:] if target[0:1] == '.': target = target[1:] refnode['refspecific'] = True return title, target class QuirrelDomain(Domain): """Quirrel language domain.""" name = 'sq' label = 'Quirrel' # if you add a new object type make sure to edit SQObject.get_index_string object_types = { 'function': ObjType(_('function'), 'func'), 'method': ObjType(_('method'), 'meth'), 'class': ObjType(_('class'), 'class'), 'data': ObjType(_('data'), 'data'), 'attribute': ObjType(_('attribute'), 'attr'), 'module': ObjType(_('module'), 'mod'), } directives = { 'function': SQCallable, 'method': SQCallable, 'class': SQConstructor, 'data': SQObject, 'attribute': SQObject, 'module': SQModule, } roles = { 'func': SQXRefRole(fix_parens=True), 'meth': SQXRefRole(fix_parens=True), 'class': SQXRefRole(fix_parens=True), 'data': SQXRefRole(), 'attr': SQXRefRole(), 'mod': SQXRefRole(), } initial_data = { 'objects': {}, # fullname -> docname, objtype 'modules': {}, # mod_name -> docname } # type: Dict[unicode, Dict[unicode, Tuple[unicode, unicode]]] def clear_doc(self, docname): # type: (unicode) -> None for fullname, (pkg_docname, _l) in list(self.data['objects'].items()): if pkg_docname == docname: del self.data['objects'][fullname] for mod_name, pkg_docname in list(self.data['modules'].items()): if pkg_docname == docname: del self.data['modules'][mod_name] def merge_domaindata(self, docnames, otherdata): # type: (List[unicode], Dict) -> None # XXX check duplicates for fullname, (fn, objtype) in otherdata['objects'].items(): if fn in docnames: self.data['objects'][fullname] = (fn, objtype) for mod_name, pkg_docname in otherdata['modules'].items(): if pkg_docname in docnames: self.data['modules'][mod_name] = pkg_docname def find_obj(self, env, mod_name, prefix, name, typ, searchorder=0): # type: (BuildEnvironment, unicode, unicode, unicode, unicode, int) -> Tuple[unicode, Tuple[unicode, unicode]] # NOQA if name[-2:] == '()': name = name[:-2] objects = self.data['objects'] searches = [] if mod_name and prefix: searches.append('.'.join([mod_name, prefix, name])) if mod_name: searches.append('.'.join([mod_name, name])) if prefix: searches.append('.'.join([prefix, name])) searches.append(name) if searchorder == 0: searches.reverse() newname = None for search_name in searches: if search_name in objects: newname = search_name return newname, objects.get(newname) def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): # type: (BuildEnvironment, unicode, Builder, unicode, unicode, nodes.Node, nodes.Node) -> nodes.Node # NOQA mod_name = node.get('sq:module') prefix = node.get('sq:object') searchorder = node.hasattr('refspecific') and 1 or 0 name, obj = self.find_obj(env, mod_name, prefix, target, typ, searchorder) if not obj: return None return make_refnode(builder, fromdocname, obj[0], name.replace('$', '_S_'), contnode, name) def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode): # type: (BuildEnvironment, unicode, Builder, unicode, nodes.Node, nodes.Node) -> List[Tuple[unicode, nodes.Node]] # NOQA mod_name = node.get('sq:module') prefix = node.get('sq:object') name, obj = self.find_obj(env, mod_name, prefix, target, None, 1) if not obj: return [] return [('sq:' + self.role_for_objtype(obj[1]), make_refnode(builder, fromdocname, obj[0], name.replace('$', '_S_'), contnode, name))] def get_objects(self): # type: () -> Iterator[Tuple[unicode, unicode, unicode, unicode, unicode, int]] for refname, (docname, type) in list(self.data['objects'].items()): yield refname, refname, type, docname, \ refname.replace('$', '_S_'), 1 def get_full_qualified_name(self, node): # type: (nodes.Node) -> unicode modname = node.get('sq:module') prefix = node.get('sq:object') target = node.get('reftarget') if target is None: return None else: return '.'.join(filter(None, [modname, prefix, target])) def setup(app): # type: (Sphinx) -> Dict[unicode, Any] app.add_domain(QuirrelDomain) return { 'version': 'builtin', 'env_version': 1, 'parallel_read_safe': True, 'parallel_write_safe': True, } myst_heading_anchors = 3 myst_enable_extensions = [ "amsmath", "colon_fence", "deflist", "dollarmath", "fieldlist", "html_admonition", "html_image", # "linkify", "replacements", "smartquotes", "strikethrough", "substitution", "tasklist", ] ================================================ FILE: doc/source/quirrel_pygment_lexer.py ================================================ """ pygments.lexers.quirrel ~~~~~~~~~~~~~~~~~~~~~~~~~~ Lexers for Quirrel. :copyright: Copyright 2022 by the Gaijin. :license: BSD, see LICENSE for details. """ import re from pygments.lexer import bygroups, combined, default, do_insertions, include, \ inherit, Lexer, RegexLexer, this, using, words from pygments.token import Text, Comment, Operator, Keyword, Name, String, \ Number, Punctuation, Other, Generic, Whitespace from pygments.util import get_bool_opt import pygments.unistring as uni from sphinx.highlighting import lexers __all__ = ['QuirrelLexer'] SQ_IDENT_START = ('(?:[$_' + uni.combine('Lu', 'Ll', 'Lt', 'Lm', 'Lo', 'Nl') + ']|\\\\u[a-fA-F0-9]{4})') SQ_IDENT_PART = ('(?:[$' + uni.combine('Lu', 'Ll', 'Lt', 'Lm', 'Lo', 'Nl', 'Mn', 'Mc', 'Nd', 'Pc') + '\u200c\u200d]|\\\\u[a-fA-F0-9]{4})') SQ_IDENT = SQ_IDENT_START + '(?:' + SQ_IDENT_PART + ')*' line_re = re.compile('.*?\n') class QuirrelLexer(RegexLexer): """ For Quirrel source code. """ name = 'Quirrel' url = 'https://quirrel.io' aliases = ['javascript', 'js'] filenames = ['*.nut'] mimetypes = ['application/quirrel', 'application/squirrel'] flags = re.DOTALL | re.MULTILINE tokens = { 'commentsandwhitespace': [ (r'\s+', Whitespace), (r' ``this.foo(x,y)`` ``table.foo(x,y)`` ---> call ``foo`` with ``(table,x,y)`` It may also help to consider why it works this way: it was initially designed to assist with object-oriented style. When calling ``foo(x,y)`` it was assumed you're calling another member of the object (or of the file) and so should operate on the same object. When calling ``mytable.foo(x,y)`` it is written plainly that you're calling a member of a different object. --------------------------------------------- Binding an environment to a function --------------------------------------------- .. index:: single: Binding an environment to a function while by default a quirrel function call passes as environment object ``this``, the object from which the function was indexed. However, it is also possible to statically bind an environment to a closure using the built-in method ``closure.bindenv(env_obj)``. The method ``bindenv()`` returns a new instance of a closure with the environment bound to it. When an environment object is bound to a function, every time the function is invoked, its ``this`` parameter will always be the previously bound environment. This mechanism is useful to implement callback systems similar to C# delegates. .. note:: The closure keeps a weak reference to the bound environment object, because of this if the object is deleted, the next call to the closure will result in a ``null`` environment object. --------------------------------------------- Lambda Expressions --------------------------------------------- .. index:: single: Lambda Expressions :: exp := '@' '(' paramlist ')' exp Lambda expressions are syntactic sugar to quickly define a function that consists of a single expression. This feature comes in handy when functional programming patterns are applied, like map/reduce or passing a compare method to array.sort(). here is a lambda expression:: let myexp = @(a,b) a + b that is equivalent to:: let myexp = function(a,b) { return a + b } a more useful usage could be:: let arr = [2,3,5,8,3,5,1,2,6] arr.sort(@(a,b) a <=> b) arr.sort(@(a,b) -(a <=> b)) that could have been written as:: let arr = [2,3,5,8,3,5,1,2,6] arr.sort(function(a,b) { return a <=> b } ) arr.sort(function(a,b) { return -(a <=> b) } ) other than being limited to a single expression lambdas support all features of regular functions. in fact they are implemented as a compile-time feature. --------------------------------------------- Free Variables --------------------------------------------- .. index:: single: Free Variables A free variable is a variable external to the function scope and is not a local variable or parameter of the function. Free variables reference a local variable from an outer scope. In the following example the variables ``testy``, ``x`` and ``y`` are bound to the function ``foo``.:: local x = 10 local y = 20 let testy = "I'm testy" let function foo(a,b) { print(testy) return a+b+x+y } A program can read or write a free variable. --------------------------------------------- Tail Recursion --------------------------------------------- .. index:: single: Tail Recursion Tail recursion is a method for partially transforming recursion in a program into iteration: it applies when the recursive calls in a function are the last executed statements in that function (just before the return). If this happens the quirrel interpreter collapses the caller stack frame before the recursive call; because of that very deep recursions are possible without risk of a stack overflow.:: function loopy(n) { if (n > 0) { println($"n={n}") return loopy(n-1) } } loopy(1000) ================================================ FILE: doc/source/reference/language/generators.rst ================================================ .. _generators: ================= Generators ================= .. index:: single: Generators A function that contains a ``yield`` statement is called *'generator function'* . When a generator function is called, it does not execute the function body, instead it returns a new suspended generator. The returned generator can be resumed through the resume statement while it is alive. The yield keyword, suspends the execution of a generator and optionally returns the result of an expression to the function that resumed the generator. The generator dies when it returns, this can happen through an explicit return statement or by exiting the function body; If an unhandled exception (or runtime error) occurs while a generator is running, the generator will automatically die. A dead generator cannot be resumed anymore.:: function geny(n) { for (local i=1; i<=n; ++i) yield i return null } let gtor = geny(10) for (local x=resume gtor; x; x=resume gtor) print(x+"\n") the output of this program will be:: 1 2 3 4 5 6 7 8 9 10 generators can also be iterated using the foreach statement. When a generator is evaluated by ``foreach``, the generator will be resumed for each iteration until it returns. The value returned by the ``return`` statement will be ignored. .. note:: A suspended generator will hold a strong reference to all the values stored in its local variables except the ``this`` object that is only a weak reference. A running generator holds a strong reference also to the ``this`` object. ================================================ FILE: doc/source/reference/language/lexical_structure.rst ================================================ .. _lexical_structure: ================= Lexical Structure ================= .. index:: single: lexical structure ----------- Identifiers ----------- .. index:: single: identifiers Identifiers start with an alphabetic character or the symbol '_' followed by any number of alphabetic characters, '_' or digits ([0-9]). Quirrel is a case sensitive language meaning that the lowercase and uppercase representation of the same alphabetic character are considered different characters. For instance, "foo", "Foo" and "fOo" are treated as 3 distinct identifiers. ----------- Keywords ----------- .. index:: single: keywords The following words are reserved and cannot be used as identifiers: +------------+------------+-----------+------------+------------+-------------+ | base | break | case | catch | class | clone | +------------+------------+-----------+------------+------------+-------------+ | continue | const | default | delete | else | enum | +------------+------------+-----------+------------+------------+-------------+ | for | foreach | function | if | in | | +------------+------------+-----------+------------+------------+-------------+ | local | null | resume | return | switch | this | +------------+------------+-----------+------------+------------+-------------+ | throw | try | typeof | while | yield | constructor | +------------+------------+-----------+------------+------------+-------------+ | instanceof | true | false | static | __LINE__ | __FILE__ | +------------+------------+-----------+------------+------------+-------------+ | let | | | | | | +------------+------------+-----------+------------+------------+-------------+ Keywords are covered in detail later in this document. ----------- Operators ----------- .. index:: single: operators Quirrel recognizes the following operators: +----------+----------+----------+----------+----------+----------+----------+----------+ | ``!`` | ``!=`` | ``||`` | ``==`` | ``&&`` | ``>=`` | ``<=`` | ``>`` | +----------+----------+----------+----------+----------+----------+----------+----------+ | ``<=>`` | ``+`` | ``+=`` | ``-`` | ``-=`` | ``/`` | ``/=`` | ``*`` | +----------+----------+----------+----------+----------+----------+----------+----------+ | ``*=`` | ``%`` | ``%=`` | ``++`` | ``--`` | ``<-`` | ``=`` | ``&`` | +----------+----------+----------+----------+----------+----------+----------+----------+ | ``^`` | ``|`` | ``~`` | ``>>`` | ``<<`` | ``>>>`` | ``??`` | | +----------+----------+----------+----------+----------+----------+----------+----------+ ------------ Other tokens ------------ .. index:: single: delimiters single: other tokens Other significant tokens are: +----------+----------+----------+----------+----------+----------+ | ``{`` | ``}`` | ``[`` | ``]`` | ``.`` | ``:`` | +----------+----------+----------+----------+----------+----------+ | ``::`` | ``'`` | ``;`` | ``"`` | ``@"`` | | +----------+----------+----------+----------+----------+----------+ ----------- Literals ----------- .. index:: single: literals single: string literals single: numeric literals Quirrel accepts integer numbers, floating point numbers and string literals. +-------------------------------+------------------------------------------+ | ``34`` | Integer number(base 10) | +-------------------------------+------------------------------------------+ | ``0xFF00A120`` | Integer number(base 16) | +-------------------------------+------------------------------------------+ | ``0753`` | Integer number(base 8) | +-------------------------------+------------------------------------------+ | ``'a'`` | Integer number | +-------------------------------+------------------------------------------+ | ``1.52`` | Floating point number | +-------------------------------+------------------------------------------+ | ``1.e2`` | Floating point number | +-------------------------------+------------------------------------------+ | ``1.e-2`` | Floating point number | +-------------------------------+------------------------------------------+ | ``"I'm a string"`` | String | +-------------------------------+------------------------------------------+ | ``@"I'm a verbatim string"`` | String | +-------------------------------+------------------------------------------+ | ``@" I'm a`` | | | ``multiline verbatim string`` | | | ``"`` | String | +-------------------------------+------------------------------------------+ Pesudo BNF .. productionlist:: IntegerLiteral : [1-9][0-9]* | '0x' [0-9A-Fa-f]+ | ''' [.]+ ''' | 0[0-7]+ FloatLiteral : [0-9]+ '.' [0-9]+ FloatLiteralExp : [0-9]+ '.' 'e'|'E' '+'|'-' [0-9]+ StringLiteral: '"'[.]* '"' VerbatimStringLiteral: '@''"'[.]* '"' ----------- Comments ----------- .. index:: single: comments A comment is text that the compiler ignores but that is useful for programmers. Comments are normally used to embed annotations in the code. The compiler treats them as white space. A comment can be ``/*`` (slash, asterisk) characters, followed by any sequence of characters (including new lines), followed by the ``*/`` characters. This syntax is the same as ANSI C.:: /* this is a multiline comment. this lines will be ignored by the compiler */ A comment can also be ``//`` (two slashes) characters, followed by any sequence of characters. A new line not immediately preceded by a backslash terminates this form of comment. It is commonly called a *"single-line comment."*:: //this is a single line comment. this line will be ignored by the compiler ================================================ FILE: doc/source/reference/language/limitations.rst ================================================ .. _limitation: ================= Limitations ================= .. index:: single: Limitations Quirrel has a number of limitations - Maximum number of local variables (including `let`-bindings) is **256** - Maximum nesting level of code tree is **500** ================================================ FILE: doc/source/reference/language/metamethods.rst ================================================ .. _metamethods: ----------- Metamethods ----------- Metamethods are a mechanism that allows the customization of certain aspects of the language semantics. Those methods are normal functions placed in a class declaration; It is possible to change many aspects of a table/class instance behavior by just defining a metamethod. For example when we use relational operators other than '==' on 2 instances, the VM will check if the class has a method in his parent called '_cmp'; if so it will call it to determine the relation between the objects.:: class Comparable { constructor(n) { name = n; } function _cmp(other) { if(nameother.name) return 1; return 0; } name = null; } let a = Comparable("Alberto"); let b = Comparable("Wouter"); if(a>b) print("a>b") else print("b<=a"); ^^^^^ _set ^^^^^ :: _set(idx,val) invoked when the index idx is not present in the object or in its delegate chain. ``_set`` must 'throw null' to notify that a key wasn't found but the there were not runtime errors (clean failure). This allows the program to differentiate between a runtime error and a 'index not found'. ^^^^^ _get ^^^^^ :: _get(idx) invoked when the index idx is not present in the object or in its delegate chain. _get must 'throw null' to notify that a key wasn't found but the there were not runtime errors (clean failure). This allows the program to differentiate between a runtime error and a 'index not found'. ^^^^^^^^^ _newslot ^^^^^^^^^ :: _newslot(key,value) invoked when a script tries to add a new slot in a table. if the slot already exists in the target table the method will not be invoked also if the "new slot" operator is used. ^^^^^^^^^ _delslot ^^^^^^^^^ :: _delslot(key) invoked when a script deletes a slot from a table. if the method is invoked quirrel will not try to delete the slot himself ^^^^^^^^ _add ^^^^^^^^ :: _add(other) the + operator returns this + other ^^^^^^^^^^^^^^^^^^^^^^^^ _sub ^^^^^^^^^^^^^^^^^^^^^^^^ :: _sub(other) the - operator (like _add) ^^^^^^^^^^^^^^^^^^^^^^^^ _mul ^^^^^^^^^^^^^^^^^^^^^^^^ :: _mul(other) the ``*`` operator (like _add) ^^^^^^^^^^^^^^^^^^^^^^^^ _div ^^^^^^^^^^^^^^^^^^^^^^^^ :: _div(other) the ``/`` operator (like _add) ^^^^^^^^^^^^^^^^^^^^^^^^ _modulo ^^^^^^^^^^^^^^^^^^^^^^^^ :: _modulo(other) the ``%`` operator (like _add) ^^^^^^^^^ _unm ^^^^^^^^^ :: _unm() the unary minus operator ^^^^^^^^^^^^^^^^^^^^^^^^ _typeof ^^^^^^^^^^^^^^^^^^^^^^^^ :: _typeof() invoked by the typeof operator on tables, userdata, and class instances. Returns the type of ``this`` as string ^^^^^^^^^^^^^^^^^^^^^^^^ _cmp ^^^^^^^^^^^^^^^^^^^^^^^^ :: _cmp(other) invoked to emulate the ``< > <= >=`` and ``<=>`` operators returns an integer as follow: +-----------+----------------------------+ | returns | relationship | +===========+============================+ | > 0 | if ``this`` > ``other`` | +-----------+----------------------------+ | 0 | if ``this`` == ``other`` | +-----------+----------------------------+ | < 0 | if ``this`` < ``other`` | +-----------+----------------------------+ ^^^^^^^^^^^^^^^^^^^^^^^^ _call ^^^^^^^^^^^^^^^^^^^^^^^^ :: _call(other) invoked when a table, userdata, or class instance is called ^^^^^^^^^^^^^^^^^^^^^^^^ _cloned ^^^^^^^^^^^^^^^^^^^^^^^^ :: _cloned(original) invoked when a table or class instance is cloned(in the cloned table) ^^^^^^^^^^^^^^^^^^^^^^^^ _nexti ^^^^^^^^^^^^^^^^^^^^^^^^ :: _nexti(previdx) invoked when a userdata or class instance is iterated by a foreach loop. If previdx==null it means that it is the first iteration. The function has to return the index of the 'next' value. ^^^^^^^^^^^^^^^^^^^^^^^^ _tostring ^^^^^^^^^^^^^^^^^^^^^^^^ :: _tostring() Invoked when during string concatenation or when the ``print`` function prints a table, instance, or userdata. The method is also invoked by the sq_tostring() API. Must return a string representation of the object. ^^^^^^^^^^^^^^^^^^^^^^^^ _lock ^^^^^^^^^^^^^^^^^^^^^^^^ :: _lock() Metamethods of class object only. Called when a class is locked (manually by an explicit call to ``cls.lock()``, when the class is being inherited or when its instance is first created). ``this`` references the class object itself. At this stage the class still can be modified. ================================================ FILE: doc/source/reference/language/statements.rst ================================================ .. _statements: ================= Statements ================= .. index:: single: statements A quirrel program is a simple sequence of statements.:: stats := stat [';'|'\n'] stats Statements in quirrel are comparable to the C-Family languages (C/C++, Java, C# etc...): assignment, function calls, program flow control structures etc.. plus some custom statement like yield, table and array constructors (All those will be covered in detail later in this document). Statements can be separated with a new line or ';' (or with the keywords case or default if inside a switch/case statement), both symbols are not required if the statement is followed by '}'. ------ Block ------ .. index:: pair: block; statement :: stat := '{' stats '}' A sequence of statements delimited by curly brackets ({ }) is called block; a block is a statement itself. ----------------------- Control Flow Statements ----------------------- .. index:: single: control flow statements quirrel implements the most common control flow statements: ``if, while, do-while, switch-case, for, foreach`` ^^^^^^^^^^^^^^ true and false ^^^^^^^^^^^^^^ .. index:: single: true and false single: true single: false Quirrel has a boolean type (bool) however like C++ it considers null, 0(integer) and 0.0(float) as *false*, any other value is considered *true*. ^^^^^^^^^^^^^^^^^ if/else statement ^^^^^^^^^^^^^^^^^ .. index:: pair: if/else; statement pair: if; statement pair: else; statement :: stat := 'if' '(' [decl ';'] exp ')' stat ['else' stat] Conditionally execute a statement depending on the result of an expression. Optionally, a variable may be declared before the condition using a declaration syntax (`local` or `let`). The declared variable is scoped to the entire if-else block (including all `else` and `else if` branches) and is destroyed after the block ends. Examples:: if (a > b) a = b else b = a if ( a == 10 ) { b = a + b return a } if (local cv = iv) println(cv) if (let cv: int|null = iv) println(cv) else println(cv - 1) if (local cv: int = iv; cv > -1000000) println(cv) if (let cv = iv) println(cv) else if (local cv2 = iv) println("fail") ^^^^^^^^^^^^^^^^^ while statement ^^^^^^^^^^^^^^^^^ .. index:: pair: while; statement :: stat:= 'while' '(' exp ')' stat Executes a statement while the condition is true.:: function testy(n) { local a = 0 while ( a < n ) a+=1 while(1) { if ( a < 0 ) break; a -= 1; } } ^^^^^^^^^^^^^^^^^^ do/while statement ^^^^^^^^^^^^^^^^^^ .. index:: pair: do/while; statement :: stat:= 'do' stat 'while' '(' expression ')' Executes a statement once, and then repeats execution of the statement until a condition expression evaluates to false.:: local a=0 do { println(a) a += 1 } while(a>100) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ switch statement (deprecated, use ``if``) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. index:: pair: switch; statement :: stat := 'switch' ''( exp ')' '{' 'case' case_exp ':' stats ['default' ':' stats] '}' Switch is a control statement allows multiple selections of code by passing control to one of the case statements within its body. The control is transferred to the case label whose case_exp matches with exp if none of the case match will jump to the default label (if present). A switch statement can contain any number if case instances, if 2 case have the same expression result the first one will be taken in account first. The default label is only allowed once and must be the last one. A break statement will jump outside the switch block. ----- Loops ----- .. index:: single: Loops ^^^^^^^^ for ^^^^^^^^ .. index:: pair: for; statement :: stat:= 'for' '(' [initexp] ';' [condexp] ';' [incexp] ')' statement Executes a statement as long as a condition is different than false.:: for (local a=0;a<10;a+=1) println(a) //or glob <- null for (glob=0;glob<10;glob+=1) { println(glob) } //or for (;;) { println("loops forever") } ^^^^^^^^ foreach ^^^^^^^^ .. index:: pair: foreach; statement :: value_pat := id | '[' destr_fields ']' | '{' destr_fields '}' 'foreach' '(' [index_id','] value_pat 'in' exp ')' stat Executes a statement for every element contained in an array, table, class, string or generator. If exp is a generator it will be resumed every iteration as long as it is alive; the value will be the result of 'resume' and the index the sequence number of the iteration starting from 0.:: let a=[10,23,33,41,589,56] foreach (idx, val in a) println($"index={idx} value={val}") //or foreach (val in a) println ($"value={val}") The iteration value may also be a destructuring pattern, in which case the yielded value is unpacked into the named bindings on each iteration. Default values and type annotations are supported in the same way as for :ref:`destructuring assignments `.:: foreach ([a, b] in [[1, 2], [3, 4]]) println(a, b) foreach (i, {x, y} in [{x = 1, y = 2}, {x = 3, y = 4}]) println(i, x, y) foreach ({n: int = 0} in [{n = 5}, {}]) println(n) See :ref:`destructuring_assignment` for the full pattern syntax. ------- break ------- .. index:: pair: break; statement :: stat := 'break' The break statement terminates the execution of a loop (for, foreach, while or do/while) or jumps out of switch statement; --------- continue --------- .. index:: pair: continue; statement :: stat := 'continue' The continue operator jumps to the next iteration of the loop skipping the execution of the following statements. --------- return --------- .. index:: pair: return; statement :: stat:= return [exp] The return statement terminates the execution of the current function/generator and optionally returns the result of an expression. If the expression is omitted the function will return null. If the return statement is used inside a generator, the generator will not be resumable anymore. --------- yield --------- .. index:: pair: yield; statement :: stat := yield [exp] (see :ref:`Generators `). -------------------------------------- Local variables declaration ( local ) -------------------------------------- .. index:: pair: Local variables declaration; statement :: initz := id [= exp][',' initz] stat := 'local' initz Local variables can be declared at any point in the program; they exist between their declaration to the end of the block where they have been declared. *EXCEPTION:* a local declaration statement is allowed as first expression in a for loop.:: for (local a=0; a<10; a+=1) print(a) ----------------------------------- Named bindings declaration ( let ) ----------------------------------- .. index:: pair: Named bindings declaration; statement :: initz := id = exp[',' initz] stat := 'let' initz Named bindings like Local variables can be declared at any point in the program; they exist between their declaration to the end of the block where they have been declared. Named bindings MUST be initialized. Named bindings works *exactly* as `const` in JavaScript ES6 (Note - big there is siginificant difference with `const` in Squirrel). The main difference between local variables and named bindings is that named bindings can't be reassigned. So if you see somewhere in function scope let foo = you can be sure that foo will always reference object on initialization :: local foo = 2 foo = 3 print(foo) //3 let foo = 2 foo = 3 //error in script compilation .. note:: While named bindings looks like constants they do not provide immutability. Named bindings can reference mutable objects (like array or instance or table) -------------------- Function declaration -------------------- .. index:: pair: Function declaration; statement :: funcname := id ['::' id] stat:= 'function' id + '(' args ')' '{' stat '}' creates a new function. ----------------- Class declaration ----------------- .. index:: pair: Class declaration; statement :: memberdecl := id '=' exp [';'] | '[' exp ']' '=' exp [';'] | functionstat | 'constructor' functionexp stat:= 'class' derefexp ['(' derefexp ')'] '{' [memberdecl] '}' creates a new class. ----------- try/catch ----------- .. index:: pair: try/catch; statement :: stat:= 'try' stat 'catch' '(' id ')' stat The try statement encloses a block of code in which an exceptional condition can occur, such as a runtime error or a throw statement. The catch clause provides the exception-handling code. When a catch clause catches an exception, its id is bound to that exception. ----------- throw ----------- .. index:: pair: throw; statement :: stat:= 'throw' exp Throws an exception. Any value can be thrown. -------------- const -------------- .. index:: pair: const; statement :: stat:= 'const' id '=' 'Integer | Float | StringLiteral Declares a constant (see :ref:`Constants & Enumerations `). -------------- enum -------------- .. index:: pair: enum; statement :: enumerations := ( 'id' '=' Integer | Float | StringLiteral ) [','] stat:= 'enum' id '{' enumerations '}' Declares an enumeration (see :ref:`Constants & Enumerations `). -------------------- Expression statement -------------------- .. index:: pair: Expression statement; statement :: stat := exp In Quirrel every expression is also allowed as statement, if so, the result of the expression is thrown away. ================================================ FILE: doc/source/reference/language/string_interpolation.rst ================================================ .. _string_interpolation: ======================== $ - String interpolation ======================== .. index:: single: String interpolation The ``$`` special character identifies a string literal as an interpolated string. An interpolated string is a string literal that might contain interpolation expressions. String interpolation provides a more readable and convenient syntax to create formatted strings than a string concatenation or ``subst()`` method. The following example uses both features to produce the same output: :: local x = 123 local y = 567 print("x = {0}, sin(y) = {1}".subst(x, math.sin(y))) print($"x = {x}, sin(y) = {math.sin(y)}") Internally string interpolation translates to ``subst()`` call. Nested interpolation strings are not supported. To use curly brackets {} inside interpolation string, ``{`` and ``}`` characters should be escaped using ``\`` :: local foo = 123 print($"\{ foo = {foo} \}") // will output: { foo = 123 } ================================================ FILE: doc/source/reference/language/tables.rst ================================================ .. _tables: ================= Tables ================= .. index:: single: Tables Tables are associative containers implemented as pairs of key/value (called slot); values can be any possible type and keys any type except 'null'. Tables are quirrel's skeleton, delegation and many other features are all implemented through this type; even the environment, where "global" variables are stored, is a table (known as root table). ------------------ Construction ------------------ Tables are created through the table constructor (see :ref:`Table constructor `) ------------------ Slot creation ------------------ .. index:: single: Slot Creation(table) Adding a new slot in a existing table is done through the "new slot" operator ``<-``; this operator behaves like a normal assignment except that if the slot does not exists it will be created.:: let a = {} The following line will cause an exception because the slot named 'newslot' does not exist in the table 'a':: a.newslot = 1234 this will succeed: :: a.newslot <- 1234; or:: a[1] <- "I'm the value of the new slot"; ----------------- Slot deletion ----------------- .. index:: single: Slot Deletion(table) :: exp:= delete derefexp Deletion of a slot is done through the keyword delete; the result of this expression will be the value of the deleted slot.:: a <- { test1=1234 deleteme="now" } delete a.test1 print(delete a.deleteme); //this will print the string "now" Note: Usage of this method could be prohibited with ``#forbid-delete-operator`` ================================================ FILE: doc/source/reference/language/threads.rst ================================================ .. _threads: ======================== Threads ======================== .. index:: single: Threads Quirrel supports cooperative threads(also known as coroutines). A cooperative thread is a subroutine that can suspended in mid-execution and provide a value to the caller without returning program flow, then its execution can be resumed later from the same point where it was suspended. At first look a Quirrel thread can be confused with a generator, in fact their behaviour is quite similar. However while a generator runs in the caller stack and can suspend only the local routine stack a thread has its own execution stack, global table and error handler; This allows a thread to suspend nested calls and have its own error policies. ------------------ Using threads ------------------ .. index:: single: Using Threads Threads are created through the built-in function 'newthread(func)'; this function gets as parameter a quirrel function and bind it to the new thread objects (will be the thread body). The returned thread object is initially in 'idle' state. the thread can be started with the function 'threadobj.call()'; the parameters passed to 'call' are passed to the thread function. A thread can be be suspended calling the function suspend(), when this happens the function that wokeup(or started) the thread returns (If a parameter is passed to suspend() it will be the return value of the wakeup function , if no parameter is passed the return value will be null). A suspended thread can be resumed calling the function 'threadobj.wakeup', when this happens the function that suspended the thread will return(if a parameter is passed to wakeup it will be the return value of the suspend function, if no parameter is passed the return value will be null). A thread terminates when its main function returns or when an unhandled exception occurs during its execution.:: function coroutine_test(a, b) { println($"{a} {b}") local ret = suspend("suspend 1") println($"the coroutine says {ret}") ret = suspend("suspend 2") println($"the coroutine says {ret}") ret = suspend("suspend 3") println($"the coroutine says {ret}") return "I'm done" } local coro = newthread(coroutine_test) local susparam = coro.call("test","coroutine") //starts the coroutine local i = 1 do { println($"suspend passed {susparam}") susparam = coro.wakeup("ciao "+i) ++i } while(coro.getstatus()=="suspended") println($"return passed {susparam}") the result of this program will be:: test coroutine suspend passed (suspend 1) the coroutine says ciao 1 suspend passed (suspend 2) the coroutine says ciao 2 suspend passed (suspend 3) the coroutine says ciao 3 return passed (I'm done). the following is an interesting example of how threads and tail recursion can be combined.:: function state1() { suspend("state1") return state2() //tail call } function state2() { suspend("state2") return state3() //tail call } function state3() { suspend("state3") return state1() //tail call } local statethread = newthread(state1) println(statethread.call()) for (local i = 0; i < 10000; i++) println(statethread.wakeup()) ================================================ FILE: doc/source/reference/language/type_annotations.rst ================================================ .. _type_annotations: Type Annotations in Quirrel =========================== Quirrel extends the Squirrel language with optional static type annotations that are enforced at runtime. Type checks occur during function calls, return statements, assignments, and destructuring operations, providing safety without requiring a separate compilation step. Basic Type Syntax ----------------- Primitive Types ~~~~~~~~~~~~~~~ Quirrel supports the following built-in types: - ``int`` - Signed integer - ``float`` - Floating-point number - ``number`` - Union of ``int | float`` (numeric values) - ``bool`` - Boolean values (``true`` / ``false``) - ``string`` - Immutable string values - ``null`` - Null value type Complex Types ~~~~~~~~~~~~~ - ``table`` - Associative array / object - ``array`` - Ordered sequence container - ``function`` - Callable function or closure - ``userdata`` - Opaque C/C++ data reference - ``generator`` - Coroutine/generator object - ``userpointer`` - Raw pointer value - ``thread`` - Execution thread - ``instance`` - Class instance - ``class`` - Class definition object - ``weakref`` - Weak reference to an object - ``any`` - Accepts any type (opt-out of type checking) Type Unions ~~~~~~~~~~~ Combine multiple types using the pipe operator (``|``): .. code-block:: quirrel local value: int|float = 42 local maybeString: string|null = null local numeric: number = 3.14 // equivalent to int|float Parentheses may be used for clarity in complex unions: .. code-block:: quirrel local x: (int | null) = null local y: (string | table | null) = {} Function Type Annotations ------------------------- Parameters and Return Types ~~~~~~~~~~~~~~~~~~~~~~~~~~~ Annotate parameters and return values with type specifiers: .. code-block:: quirrel function add(x: int, y: int): int { return x + y } function toString(value: any): string { return value.tostring() } Nullable Parameters with Defaults ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Combine unions with default values for optional parameters: .. code-block:: quirrel function greet(name: string|null = null): string { return name ? "Hello " + name : "Hello guest" } Vararg Type Annotations ~~~~~~~~~~~~~~~~~~~~~~~ Specify the expected type for variable arguments using ``...``: .. code-block:: quirrel function sum(first: number, ...: number): number { local total = first foreach (v in vargv) total += v return total } Lambda/Anonymous Functions ~~~~~~~~~~~~~~~~~~~~~~~~~~ Type annotations apply to lambdas using the ``@`` syntax: .. code-block:: quirrel let multiply = @(x: int, y: int): int x * y Variable Type Annotations ------------------------- Local and Let Bindings ~~~~~~~~~~~~~~~~~~~~~~ Annotate variables at declaration site: .. code-block:: quirrel local count: int = 0 let pi: float = 3.14159 local config: table|array = {timeout = 30} Type checks occur on assignment: .. code-block:: quirrel local id: int = "not a number" // Runtime type error Destructuring with Types ------------------------ Object Destructuring ~~~~~~~~~~~~~~~~~~~~ Annotate individual properties during destructuring: .. code-block:: quirrel local { name: string, age: int|null = 25 } = userData // With explicit type unions local { x: int, y: string|int|null } = point Array Destructuring ~~~~~~~~~~~~~~~~~~~ Specify types for array elements: .. code-block:: quirrel local [first: int, second: string] = [42, "hello"] // Mixed annotated/unannotated elements local [head: int, value1, value2] = [1, 2, 3] // Complex unions in arrays local [value: (int | null), flag: bool] = [null, true] Nested Destructuring in Parameters ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Functions can destructure parameters with full type annotations: .. code-block:: quirrel function createUser([name: string, age: int], {active: bool = true}) { // ... } function process( [handler: function|null = null, config: table], {timeout: int|float = 5.0}, ... ) { // ... } Runtime Type Checking Behavior ------------------------------ Quirrel performs **dynamic type validation** at these points: 1. **Function calls** - Parameters are checked against declared types before execution 2. **Return statements** - Return values are validated against the function's return type 3. **Assignments** - Values are checked when assigned to typed variables 4. **Destructuring** - Extracted values are validated against property/element types All checks occur at runtime with immediate exceptions on mismatch: .. code-block:: quirrel function strict(x: int): int { return x } strict("wrong") // Throws: type mismatch in parameter 'x' Best Practices -------------- - Type checks add minor runtime overhead; avoid in ultra-hot loops if unneeded - Use ``number`` instead of ``int|float`` for numeric parameters accepting both types - Use ``any`` sparingly—only when truly necessary for dynamic behavior - Combine defaults with nullable types for ergonomic optional parameters: .. code-block:: quirrel function fetch(url: string, timeout: float|null = 30.0) { ... } ================================================ FILE: doc/source/reference/language/weak_references.rst ================================================ .. _weak_references: ======================== Weak References ======================== .. index:: single: Weak References The weak references allows the programmers to create references to objects without influencing the lifetime of the object itself. In quirrel Weak references are first-class objects created through the built-in method obj.weakref(). All types except null implement the weakref() method; however in bools, integers, and floats the method simply returns the object itself(this because this types are always passed by value). When a weak references is assigned to a container (table slot,array,class or instance) is treated differently than other objects; When a container slot that hold a weak reference is fetched, it always returns the value pointed by the weak reference instead of the weak reference object. This allow the programmer to ignore the fact that the value handled is weak. When the object pointed by weak reference is destroyed, the weak reference is automatically set to null. :: let t = {} let a = ["first","second","third"] //creates a weakref to the array and assigns it to a table slot t.thearray <- a.weakref() The table slot 'thearray' contains a weak reference to an array. The following line prints "first", because tables(and all other containers) always return the object pointed by a weak ref :: print(t.thearray[0]) the only strong reference to the array is owned by the local variable 'a', so because the following line assigns a integer to 'a' the array is destroyed. :: a = 123 When an object pointed by a weak ref is destroyed the weak ref is automatically set to null, so the following line will print "null". :: print(typeof(t.thearray)) ----------------------------------- Handling weak references explicitly ----------------------------------- If a weak reference is assigned to a local variable, then is treated as any other value. :: let t = {} let weakobj = t.weakref() the following line prints "weakref". :: print(typeof(weakobj)) the object pointed by the weakref can be obtained through the built-in method weakref.ref(). The following line prints "table". :: print(typeof(weakobj.ref())) ================================================ FILE: doc/source/reference/language.rst ================================================ .. _thelanguage: *************************** The language *************************** Project source code is available at https://github.com/GaijinEntertainment/quirrel .. toctree:: language/lexical_structure.rst language/datatypes.rst language/statements.rst language/expressions.rst language/tables.rst language/arrays.rst language/functions.rst language/classes.rst language/generators.rst language/constants_and_enumerations.rst language/destructuring_assignment.rst language/type_annotations.rst language/string_interpolation.rst language/threads.rst language/weak_references.rst language/metamethods.rst language/builtin_functions.rst language/compiler_directives.rst language/limitations.rst ================================================ FILE: doc/source/repl/index.rst ================================================ .. _repl: ###################################### Try Quirrel online ###################################### .. raw:: html
Source code

    
Result

    
Project source code is available at https://github.com/GaijinEntertainment/quirrel Copyright (c) 2003-2016 Alberto Demichelis Copyright (c) 2016-2024 Gaijin Games KFT ================================================ FILE: doc/source/rfcs/README.md ================================================ # RFC process ## Background Whenever Quirrel language changes its syntax or semantics (including behavior of builtin libraries), we need to consider many implications of the changes. Whenever new syntax is introduced, we need to ask: - Is it backwards compatible? - Is it easy for machines and humans to parse? - Does it follow 'Zen of Quirrel' (based on Zen of Python?) - Does it create grammar ambiguities for current and future syntax? - Is it stylistically coherent with the rest of the language? - Does it present challenges with editor integration like autocomplete? - Will it affect performance? For changes in semantics, we should be asking: - Is behavior easy to understand and non-surprising? - Can it be implemented performantly today? - Is it compatible with type checking and other forms of static analysis? For new standard library functions, we should be asking: - Is the new functionality used/useful often enough in existing code? - Does the standard library implementation carry important performance benefits that can't be achieved in user code? - Is the behavior general and unambiguous, as opposed to solving a problem / providing an interface that's too specific? - Is the function interface amenable to type checking / linting? In addition to these questions, we also need to consider that every addition carries a cost, and too many features will result in a language that is harder to learn, harder to implement and ensure consistent implementation quality throughout, slower, etc. In addition, any language is greater than the sum of its parts and features often have non-intuitive interactions with each other. Since reversing these decisions is incredibly costly and can be impossible due to backwards compatibility implications, all user facing changes to Quirrel language and core libraries must go through an RFC process. ## Process There is no any special process for RFC review at the moment. ## Implementation When an RFC gets merged, the feature *can* be implemented; however, there's no set timeline for that implementation. In some cases implementation may land in a matter of days after an RFC is merged, in some it may take months. To avoid having permanently stale RFCs, in rare cases Quirrel team can *remove* a previously merged RFC when the landscape is believed to change enough for a feature like this to warrant further discussion. When an RFC is implemented and the implementation is enabled via feature flags, RFC should be updated to include "**Status**: Implemented" at the top level (before *Summary* section). ================================================ FILE: doc/source/rfcs/STATUS.md ================================================ # RFCs This document tracks RFCs, both implemented and not implemented (implemented RFC should eventually go way from here). ## Deprecate clone operator and replace it with .clone method **Status**: Implemented, as optional behavior currently. Can be specified by #forbid-clone-operator or #allow-clone-operator ## assignments in 'if' (let and local) **Status**: Implemented ## Replace let with const Replace 'let' for immutables with 'const' keyword or make them aliases. `const` and `let` for simple types works the same from coder PoV. `let` and `const` declarations for simple types (integer, float, string, null, boolean) can be optimized (we do know in compile time what is it). For other types const should work as 'binding' (like `let` do now). **Status**: Waiting for implementation ## Add .hasindex() for instances, classes, tables, arrays and strings. Should behave as 'in' operator, but exists only for types that can have 'index', and should have correct check of arguments type (arrays and strings can have only integer as index) **Status**: Waiting for implementation ## Add .hasvalue() for tables and arrays. The same as 'contains' for arrays. To make it more consistent with findvalue() and findindex() and hasindex() **Status**: Waiting for implementation ## Add for and foreach in ranges Syntax like: ``` for (range:integer) for (start_of_range:integer, end_of_range:integer) foreach (i in range:integer) ``` Will allow to make code faster and safer than with for(local i=0; i`) to output the error. .. _sqstd_printcallstack: .. c:function:: void sqstd_printcallstack(HSQUIRRELVM v) :param HSQUIRRELVM v: the target VM prints the call stack and stack contents. the function uses the error printing function set through(:ref:`sq_setprintfunc `) to output the stack dump. .. _sqstd_formatcallstackstring: .. c:function:: void sqstd_formatcallstackstring(HSQUIRRELVM v) :param HSQUIRRELVM v: the target VM Prints the call stack and stack contents to the string and pushes resulting string to the stack. Produces the same output as (:ref:`sqstd_printcallstack `) but without using print function. ================================================ FILE: doc/source/stdlib/stddatetimelib.rst ================================================ .. _stdlib_stddatetimelib: ==================== The Datetime library ==================== The datetime library date and time manipulation facilities -------------- Quirrel API -------------- ++++++++++++++ Global Symbols ++++++++++++++ .. sq:function:: clock() returns a float representing the number of seconds elapsed since the start of the process .. sq:function:: date([time [, format]]) returns a table containing a date/time split into the slots: +-------------+----------------------------------------+ | sec | Seconds after minute (0 - 59). | +-------------+----------------------------------------+ | min | Minutes after hour (0 - 59). | +-------------+----------------------------------------+ | hour | Hours since midnight (0 - 23). | +-------------+----------------------------------------+ | day | Day of month (1 - 31). | +-------------+----------------------------------------+ | month | Month (0 - 11; January = 0). | +-------------+----------------------------------------+ | year | Year (current year). | +-------------+----------------------------------------+ | wday | Day of week (0 - 6; Sunday = 0). | +-------------+----------------------------------------+ | yday | Day of year (0 - 365; January 1 = 0). | +-------------+----------------------------------------+ if `time` is omitted the current time is used. if `format` can be 'l' local time or 'u' UTC time, if omitted is defaulted as 'l'(local time). .. sq:function:: time() returns the number of seconds elapsed since midnight 00:00:00, January 1, 1970. the result of this function can be formatted through the function `date()` -------------- C API -------------- .. _sqstd_register_datetimelib: .. c:function:: SQRESULT sqstd_register_datetimelib(HSQUIRRELVM v) :param HSQUIRRELVM v: the target VM :returns: an SQRESULT :remarks: The function expects a table on top of the stack where to register the global library functions. initialize and register the datetime library in the given VM. ================================================ FILE: doc/source/stdlib/stddebuglib.rst ================================================ .. _stdlib_stddebuglib: ======================== The Debug library ======================== the library implements some basic debug routines. -------------- Quirrel API -------------- .. sq:function:: getbuildinfo(x) returns table containing information on VM build parameters. * **version** - string values describing the version of VM and compiler. * **charsize** - size in bytes of the internal VM representation for characters(1 for ASCII builds 2 for UNICODE builds). * **intsize** - size in bytes of the internal VM representation for integers(4 for 32bits builds 8 for 64bits builds). * **floatsize** - size in bytes of the internal VM representation for floats(4 for single precision builds 8 for double precision builds). .. sq:function:: seterrorhandler(func) sets the runtime error handler .. sq:function:: setdebughook(hook_func) sets the debug hook hook_func should have follow signature: :: hook_func(hook_type:integer, source_file:string, line_num:integer, func_name:string) hook_type can be 'l' - line, 'r' - return, 'c' - call or 'x' for VM shutdown call of debughook for each line performed only when debuginfo is enabled .. sq:function:: getstackinfos(level) returns the stack informations of a given call stack level. returns a table formatted as follow: :: { func="DoStuff", //function name src="test.nut", //source file line=10, //line number locals = { //a table containing the local variables a=10, testy="I'm a string" } } level = 0 is getstackinfos() itself! level = 1 is the current function, level = 2 is the caller of the current function, and so on. If the stack level doesn't exist the function returns null. .. sq:function:: format_call_stack_string() Collects the call stack and returns the information about functions being executed and local variables as a string. .. sq:function:: getlocals([level=1], [include_internal=false]) Returns a table containing the local variables of a given call stack level. (level = 1 is the current function, level = 2 is the caller of the current function, and so on). If the stack level doesn't exist the function returns null. If include_internal is true, the table will also contain the internal variables (``foreach`` iterators, ``this`` and ``vargv``). Very similar to ``getstackinfos()``, added for convenience. .. sq:function:: collectgarbage() Runs the garbage collector and returns the number of reference cycles found (and deleted). This function only works on garbage collector builds. .. sq:function:: resurrectunreachable() Runs the garbage collector and returns an array containing all unreachable object found. If no unreachable object is found, null is returned instead. This function is meant to help debugging reference cycles. This function only works on garbage collector builds. ================================================ FILE: doc/source/stdlib/stdiolib.rst ================================================ .. _stdlib_stdiolib: ======================== The Input/Output library ======================== the i/o library implements basic input/output routines. -------------- Quirrel API -------------- ++++++++++++++ Global Symbols ++++++++++++++ .. sq:data:: stderr File object bound on the os *standard error* stream .. sq:data:: stdin File object bound on the os *standard input* stream .. sq:data:: stdout File object bound on the os *standard output* stream ++++++++++++++ The file class ++++++++++++++ The file object implements a stream on a operating system file. .. sq:class:: file(path, patten) It's constructor imitates the behaviour of the C runtime function fopen for eg. :: local myfile = file("test.xxx","wb+"); creates a file with read/write access in the current directory. .. sq:function:: file.close() closes the file. .. sq:function:: file.eos() returns a non null value if the read/write pointer is at the end of the stream. .. sq:function:: file.flush() flushes the stream.return a value != null if succeeded, otherwise returns null .. sq:function:: file.len() returns the length of the stream .. sq:function:: file.readblob(size) :param int size: number of bytes to read read n bytes from the stream and returns them as blob .. sq:function:: file.readn(type) :param int type: type of the number to read reads a number from the stream according to the type parameter. `type` can have the following values: +--------------+--------------------------------------------------------------------------------+----------------------+ | parameter | return description | return type | +==============+================================================================================+======================+ | 'l' | processor dependent, 32bits on 32bits processors, 64bits on 64bits processors | integer | +--------------+--------------------------------------------------------------------------------+----------------------+ | 'i' | 32bits number | integer | +--------------+--------------------------------------------------------------------------------+----------------------+ | 's' | 16bits signed integer | integer | +--------------+--------------------------------------------------------------------------------+----------------------+ | 'w' | 16bits unsigned integer | integer | +--------------+--------------------------------------------------------------------------------+----------------------+ | 'c' | 8bits signed integer | integer | +--------------+--------------------------------------------------------------------------------+----------------------+ | 'b' | 8bits unsigned integer | integer | +--------------+--------------------------------------------------------------------------------+----------------------+ | 'f' | 32bits float | float | +--------------+--------------------------------------------------------------------------------+----------------------+ | 'd' | 64bits float | float | +--------------+--------------------------------------------------------------------------------+----------------------+ .. sq:function:: file.resize(size) :param int size: the new size of the blob in bytes resizes the blob to the specified `size` .. sq:function:: file.seek(offset [,origin]) :param int offset: indicates the number of bytes from `origin`. :param int origin: origin of the seek +--------------+-------------------------------------------+ | 'b' | beginning of the stream | +--------------+-------------------------------------------+ | 'c' | current location | +--------------+-------------------------------------------+ | 'e' | end of the stream | +--------------+-------------------------------------------+ Moves the read/write pointer to a specified location. .. note:: If origin is omitted the parameter is defaulted as 'b'(beginning of the stream). .. sq:function:: file.tell() returns the read/write pointer absolute position .. sq:function:: file.writeblob(src) :param blob src: the source blob containing the data to be written writes a blob in the stream .. sq:function:: file.writen(n, type) :param number n: the value to be written :param int type: type of the number to write writes a number in the stream formatted according to the `type` pamraeter `type` can have the following values: +--------------+--------------------------------------------------------------------------------+ | parameter | return description | +==============+================================================================================+ | 'i' | 32bits number | +--------------+--------------------------------------------------------------------------------+ | 's' | 16bits signed integer | +--------------+--------------------------------------------------------------------------------+ | 'w' | 16bits unsigned integer | +--------------+--------------------------------------------------------------------------------+ | 'c' | 8bits signed integer | +--------------+--------------------------------------------------------------------------------+ | 'b' | 8bits unsigned integer | +--------------+--------------------------------------------------------------------------------+ | 'f' | 32bits float | +--------------+--------------------------------------------------------------------------------+ | 'd' | 64bits float | +--------------+--------------------------------------------------------------------------------+ -------------- C API -------------- .. _sqstd_register_iolib: .. c:function:: SQRESULT sqstd_register_iolib(HSQUIRRELVM v) :param HSQUIRRELVM v: the target VM :returns: an SQRESULT :remarks: The function aspects a table on top of the stack where to register the global library functions. initialize and register the io library in the given VM. ++++++++++++++ File Object ++++++++++++++ .. c:function:: SQRESULT sqstd_createfile(HSQUIRRELVM v, SQFILE file, SQBool owns) :param HSQUIRRELVM v: the target VM :param SQFILE file: the stream that will be rapresented by the file object :param SQBool owns: if different true the stream will be automatically closed when the newly create file object is destroyed. :returns: an SQRESULT creates a file object bound to the SQFILE passed as parameter and pushes it in the stack .. c:function:: SQRESULT sqstd_getfile(HSQUIRRELVM v, SQInteger idx, SQFILE* file) :param HSQUIRRELVM v: the target VM :param SQInteger idx: and index in the stack :param SQFILE* file: A pointer to a SQFILE handle that will store the result :returns: an SQRESULT retrieve the pointer of a stream handle from an arbitrary position in the stack. ++++++++++++++++++++++++++++++++ Script loading and serialization ++++++++++++++++++++++++++++++++ .. c:function:: SQRESULT sqstd_loadfile(HSQUIRRELVM v, const char* filename, SQBool printerror) :param HSQUIRRELVM v: the target VM :param char* filename: path of the script that has to be loaded :param SQBool printerror: if true the compiler error handler will be called if a error occurs :returns: an SQRESULT Compiles a squirrel script or loads a precompiled one an pushes it as closure in the stack. When squirrel is compiled in Unicode mode the function can handle different character encodings, UTF8 with and without prefix and UCS-2 prefixed(both big endian an little endian). If the source stream is not prefixed UTF8 encoding is used as default. .. c:function:: SQRESULT sqstd_dofile(HSQUIRRELVM v, const char* filename, SQBool retval, SQBool printerror) :param HSQUIRRELVM v: the target VM :param char* filename: path of the script that has to be loaded :param SQBool retval: if true the function will push the return value of the executed script in the stack. :param SQBool printerror: if true the compiler error handler will be called if a error occurs :returns: an SQRESULT :remarks: the function expects a table on top of the stack that will be used as 'this' for the execution of the script. The 'this' parameter is left untouched in the stack. Compiles a squirrel script or loads a precompiled one and executes it. Optionally pushes the return value of the executed script in the stack. When squirrel is compiled in unicode mode the function can handle different character encodings, UTF8 with and without prefix and UCS-2 prefixed(both big endian an little endian). If the source stream is not prefixed, UTF8 encoding is used as default. :: sq_pushroottable(v); //push the root table(were the globals of the script will are stored) sqstd_dofile(v, "test.nut", SQFalse, SQTrue);// also prints syntax errors if any .. c:function:: SQRESULT sqstd_writeclosuretofile(HSQUIRRELVM v, const char* filename) :param HSQUIRRELVM v: the target VM :param char* filename: destination path of serialized closure :returns: an SQRESULT serializes the closure at the top position in the stack as bytecode in the file specified by the parameter filename. If a file with the same name already exists, it will be overwritten. ================================================ FILE: doc/source/stdlib/stdiostreamlib.rst ================================================ .. _stdlib_stdbloblib: ==================== The IOstream library ==================== The iostream library implements binary data manipulations routines. The library is based on `blob objects` that represent a buffer of arbitrary binary data. --------------- Quirrel API --------------- +++++++++++++++ Global symbols +++++++++++++++ .. sq:function:: castf2i(f) casts a float to a int .. sq:function:: casti2f(n) casts a int to a float .. sq:function:: swap2(n) swap the byte order of a number (like it would be a 16bits integer) .. sq:function:: swap4(n) swap the byte order of an integer .. sq:function:: swapfloat(n) swaps the byteorder of a float ++++++++++++++++++ The blob class ++++++++++++++++++ The blob object is a buffer of arbitrary binary data. The object behaves like a file stream, it has a read/write pointer and it automatically grows if data is written out of his boundary. A blob can also be accessed byte by byte through the `[]` operator. .. sq:class:: blob(size) :param int size: initial size of the blob returns a new instance of a blob class of the specified size in bytes .. sq:function:: blob.eos() returns a non null value if the read/write pointer is at the end of the stream. .. sq:function:: blob.flush() flushes the stream.return a value != null if succeded, otherwise returns null .. sq:function:: blob.len() returns the length of the stream .. sq:function:: blob.readblob(size) :param int size: number of bytes to read read n bytes from the stream and returns them as blob .. sq:function:: blob.readn(type) :param int type: type of the number to read reads a number from the stream according to the type parameter. `type` can have the following values: +--------------+--------------------------------------------------------------------------------+----------------------+ | parameter | return description | return type | +==============+================================================================================+======================+ | 'l' | processor dependent, 32bits on 32bits processors, 64bits on 64bits processors | integer | +--------------+--------------------------------------------------------------------------------+----------------------+ | 'i' | 32bits number | integer | +--------------+--------------------------------------------------------------------------------+----------------------+ | 's' | 16bits signed integer | integer | +--------------+--------------------------------------------------------------------------------+----------------------+ | 'w' | 16bits unsigned integer | integer | +--------------+--------------------------------------------------------------------------------+----------------------+ | 'c' | 8bits signed integer | integer | +--------------+--------------------------------------------------------------------------------+----------------------+ | 'b' | 8bits unsigned integer | integer | +--------------+--------------------------------------------------------------------------------+----------------------+ | 'f' | 32bits float | float | +--------------+--------------------------------------------------------------------------------+----------------------+ | 'd' | 64bits float | float | +--------------+--------------------------------------------------------------------------------+----------------------+ .. sq:function:: blob.resize(size) :param int size: the new size of the blob in bytes resizes the blob to the specified `size` .. sq:function:: blob.seek(offset [,origin]) :param int offset: indicates the number of bytes from `origin`. :param int origin: origin of the seek +--------------+-------------------------------------------+ | 'b' | beginning of the stream | +--------------+-------------------------------------------+ | 'c' | current location | +--------------+-------------------------------------------+ | 'e' | end of the stream | +--------------+-------------------------------------------+ Moves the read/write pointer to a specified location. .. note:: If origin is omitted the parameter is defaulted as 'b'(beginning of the stream). .. sq:function:: blob.swap2() swaps the byte order of the blob content as it would be an array of `16bits integers` .. sq:function:: blob.swap4() swaps the byte order of the blob content as it would be an array of `32bits integers` .. sq:function:: blob.tell() returns the read/write pointer absolute position .. sq:function:: blob.as_string() creates string from blob .. sq:function:: blob.writeblob(src) :param blob src: the source blob containing the data to be written writes a blob in the stream .. sq:function:: blob.writestring(text) :param string text: the source string containing the data to be written writes a string in the stream .. sq:function:: blob.readobject() deserialize an object from the stream .. sq:function:: blob.writeobject(obj) :param obj object: the source object containing the data to be written serialize an object in the stream .. sq:function:: blob.writen(n, type) :param number n: the value to be written :param int type: type of the number to write writes a number in the stream formatted according to the `type` parameter `type` can have the following values: +--------------+--------------------------------------------------------------------------------+ | parameter | return description | +==============+================================================================================+ | 'i' | 32bits number | +--------------+--------------------------------------------------------------------------------+ | 's' | 16bits signed integer | +--------------+--------------------------------------------------------------------------------+ | 'w' | 16bits unsigned integer | +--------------+--------------------------------------------------------------------------------+ | 'c' | 8bits signed integer | +--------------+--------------------------------------------------------------------------------+ | 'b' | 8bits unsigned integer | +--------------+--------------------------------------------------------------------------------+ | 'f' | 32bits float | +--------------+--------------------------------------------------------------------------------+ | 'd' | 64bits float | +--------------+--------------------------------------------------------------------------------+ ------ C API ------ .. _sqstd_register_bloblib: .. c:function:: SQRESULT sqstd_register_bloblib(HSQUIRRELVM v) :param HSQUIRRELVM v: the target VM :returns: an SQRESULT :remarks: The function expects a table on top of the stack where to register the global library functions. initializes and registers the blob library in the given VM. .. _sqstd_getblob: .. c:function:: SQRESULT sqstd_getblob(HSQUIRRELVM v, SQInteger idx, SQUserPointer* ptr) :param HSQUIRRELVM v: the target VM :param SQInteger idx: and index in the stack :param SQUserPointer* ptr: A pointer to the userpointer that will point to the blob's payload :returns: an SQRESULT retrieve the pointer of a blob's payload from an arbitrary position in the stack. .. _sqstd_getblobsize: .. c:function:: SQInteger sqstd_getblobsize(HSQUIRRELVM v, SQInteger idx) :param HSQUIRRELVM v: the target VM :param SQInteger idx: and index in the stack :returns: the size of the blob at `idx` position retrieves the size of a blob's payload from an arbitrary position in the stack. .. _sqstd_createblob: .. c:function:: SQUserPointer sqstd_createblob(HSQUIRRELVM v, SQInteger size) :param HSQUIRRELVM v: the target VM :param SQInteger size: the size of the blob payload that has to be created :returns: a pointer to the newly created blob payload creates a blob with the given payload size and pushes it in the stack. ================================================ FILE: doc/source/stdlib/stdmathlib.rst ================================================ .. _stdlib_stdmathlib: ================ The Math library ================ the math lib provides basic mathematic routines. The library mimics the C runtime library implementation. ------------ Quirrel API ------------ +++++++++++++++ Global Symbols +++++++++++++++ .. sq:function:: abs(x) returns the absolute value of `x` as an integer .. sq:function:: acos(x) returns the arccosine of `x` .. sq:function:: asin(x) returns the arcsine of `x` .. sq:function:: atan(x) returns the arctangent of `x` .. sq:function:: atan2(x,y) returns the arctangent of `x/y` .. sq:function:: ceil(x) returns a float value representing the smallest integer that is greater than or equal to `x` .. sq:function:: cos(x) returns the cosine of `x` .. sq:function:: exp(x) returns the exponential value of the float parameter `x` .. sq:function:: fabs(x) returns the absolute value of `x` as a float .. sq:function:: floor(x) returns a float value representing the largest integer that is less than or equal to `x` .. sq:function:: log(x) returns the natural logarithm of `x` .. sq:function:: log10(x) returns the logarithm base-10 of `x` .. sq:function:: pow(x,y) returns `x` raised to the power of `y` .. sq:function:: rand() returns a pseudorandom integer in the range 0 to `RAND_MAX` .. sq:function:: sin(x) returns the sine of `x` .. sq:function:: sqrt(x) returns the square root of `x` .. sq:function:: srand(seed) sets the starting point for generating a series of pseudorandom integers .. sq:function:: tan(x) returns the tangent of `x` .. sq:function:: min(x, y, [z], [w], ...) returns minimal value of all arguments .. sq:function:: max(x, y, [z], [w], ...) returns maximal value of all arguments .. sq:function:: clamp(x, min_val, max_val) returns value limited by provided min-max range .. sq:data:: RAND_MAX the maximum value that can be returned by the `rand()` function .. sq:data:: PI The numeric constant pi (3.141592) is the ratio of the circumference of a circle to its diameter ------------ C API ------------ .. _sqstd_register_mathlib: .. c:function:: SQRESULT sqstd_register_mathlib(HSQUIRRELVM v) :param HSQUIRRELVM v: the target VM :returns: an SQRESULT :remarks: The function aspects a table on top of the stack where to register the global library functions. initializes and register the math library in the given VM. ================================================ FILE: doc/source/stdlib/stdstringlib.rst ================================================ .. _stdlib_stdstringlib: ================== The String library ================== the string lib implements string formatting and regular expression matching routines. -------------- Quirrel API -------------- ++++++++++++++ Global Symbols ++++++++++++++ .. sq:function:: endswith(str, cmp) returns `true` if the end of the string `str` matches a the string `cmp` otherwise returns `false` .. sq:function:: escape(str) Returns a string with backslashes before characters that need to be escaped(`\",\a,\b,\t,\n,\v,\f,\r,\\,\",\',\0,\xnn`). .. sq:function:: format(formatstr, ...) Returns a string formatted according `formatstr` and the optional parameters following it. The format string follows the same rules as the `printf` family of standard C functions( the "*" is not supported). :: e.g. sq> print(format("%s %d 0x%02X\n","this is a test :",123,10)); this is a test : 123 0x0A .. sq:function:: printf(formatstr, ...) Just like calling `print(format(formatstr` as in the example above, but is more convenient AND more efficient. :: e.g. sq> printf("%s %d 0x%02X\n","this is a test :",123,10); this is a test : 123 0x0A .. note:: The following functions are also available as string :ref:`type methods `. .. sq:function:: lstrip(str) Strips white-space-only characters that might appear at the beginning of the given string and returns the new stripped string. .. sq:function:: rstrip(str) Strips white-space-only characters that might appear at the end of the given string and returns the new stripped string. .. js:function:: split_by_chars(str, separators [, skipempty]) returns an array of strings split at each point where a separator character occurs in `str`. The separator is not returned as part of any array element. The parameter `separators` is a string that specifies the characters as to be used for the splitting. The parameter `skipempty` is a boolean (default false). If `skipempty` is true, empty strings are not added to array. :: eg. let a = split_by_chars("1.2-3;;4/5", ".-/;") // the result will be [1,2,3,,4,5] or let b = split_by_chars("1.2-3;;4/5", ".-/;", true) // the result will be [1,2,3,4,5] .. sq:function:: startswith(str, cmp) returns `true` if the beginning of the string `str` matches the string `cmp`; otherwise returns `false` .. sq:function:: strip(str) Strips white-space-only characters that might appear at the beginning or end of the given string and returns the new stripped string. ++++++++++++++++++ The regexp class ++++++++++++++++++ .. sq:class:: regexp(pattern) The regexp object represents a precompiled regular expression pattern. The object is created through `regexp(pattern)`. +---------------------+--------------------------------------+ | `\\` | Quote the next metacharacter | +---------------------+--------------------------------------+ | `^` | Match the beginning of the string | +---------------------+--------------------------------------+ | `.` | Match any character | +---------------------+--------------------------------------+ | `$` | Match the end of the string | +---------------------+--------------------------------------+ | `|` | Alternation | +---------------------+--------------------------------------+ | `(subexp)` | Grouping (creates a capture) | +---------------------+--------------------------------------+ | `(?:subexp)` | No Capture Grouping (no capture) | +---------------------+--------------------------------------+ | `[]` | Character class | +---------------------+--------------------------------------+ **GREEDY CLOSURES** +---------------------+---------------------------------------------+ | `*` | Match 0 or more times | +---------------------+---------------------------------------------+ | `+` | Match 1 or more times | +---------------------+---------------------------------------------+ | `?` | Match 1 or 0 times | +---------------------+---------------------------------------------+ | `{n}` | Match exactly n times | +---------------------+---------------------------------------------+ | `{n,}` | Match at least n times | +---------------------+---------------------------------------------+ | `{n,m}` | Match at least n but not more than m times | +---------------------+---------------------------------------------+ **ESCAPE CHARACTERS** +---------------------+--------------------------------------+ | `\\t` | tab (HT, TAB) | +---------------------+--------------------------------------+ | `\\n` | newline (LF, NL) | +---------------------+--------------------------------------+ | `\\r` | return (CR) | +---------------------+--------------------------------------+ | `\\f` | form feed (FF) | +---------------------+--------------------------------------+ **PREDEFINED CLASSES** +---------------------+--------------------------------------+ | `\\l` | lowercase next char | +---------------------+--------------------------------------+ | `\\u` | uppercase next char | +---------------------+--------------------------------------+ | `\\a` | letters | +---------------------+--------------------------------------+ | `\\A` | non letters | +---------------------+--------------------------------------+ | `\\w` | alphanumeric `[_0-9a-zA-Z]` | +---------------------+--------------------------------------+ | `\\W` | non alphanumeric `[^_0-9a-zA-Z]` | +---------------------+--------------------------------------+ | `\\s` | space | +---------------------+--------------------------------------+ | `\\S` | non space | +---------------------+--------------------------------------+ | `\\d` | digits | +---------------------+--------------------------------------+ | `\\D` | non digits | +---------------------+--------------------------------------+ | `\\x` | hexadecimal digits | +---------------------+--------------------------------------+ | `\\X` | non hexadecimal digits | +---------------------+--------------------------------------+ | `\\c` | control characters | +---------------------+--------------------------------------+ | `\\C` | non control characters | +---------------------+--------------------------------------+ | `\\p` | punctuation | +---------------------+--------------------------------------+ | `\\P` | non punctuation | +---------------------+--------------------------------------+ | `\\b` | word boundary | +---------------------+--------------------------------------+ | `\\B` | non word boundary | +---------------------+--------------------------------------+ .. sq:function:: regexp.capture(str [, start]) returns an array of tables containing two indexes ("begin" and "end") of the first match of the regular expression in the string `str`. An array entry is created for each captured sub expressions. If no match occurs returns null. The search starts from the index `start` of the string; if `start` is omitted the search starts from the beginning of the string. The first element of the returned array(index 0) always contains the complete match. :: local ex = regexp(@"(\d+) ([a-zA-Z]+)(\p)"); local string = "stuff 123 Test;"; local res = ex.capture(string); foreach(i,val in res) { println(format("match number[%02d] %s", i,string.slice(val.begin,val.end))); //prints "Test" } ... will print match number[00] 123 Test; match number[01] 123 match number[02] Test match number[03] ; .. sq:function:: regexp.match(str) returns a true if the regular expression matches the string `str`, otherwise returns false. .. sq:function:: regexp.search(str [, start]) returns a table containing two indexes ("begin" and "end") of the first match of the regular expression in the string `str`, otherwise if no match occurs returns null. The search starts from the index `start` of the string; if `start` is omitted the search starts from the beginning of the string. :: local ex = regexp("[a-zA-Z]+"); local string = "123 Test;"; local res = ex.search(string); print(string.slice(res.begin,res.end)); //prints "Test" ------------- C API ------------- .. _sqstd_register_stringlib: .. c:function:: SQRESULT sqstd_register_stringlib(HSQUIRRELVM v) :param HSQUIRRELVM v: the target VM :returns: an SQRESULT :remarks: The function aspects a table on top of the stack where to register the global library functions. initialize and register the string library in the given VM. +++++++++++++ Formatting +++++++++++++ .. c:function:: SQRESULT sqstd_format(HSQUIRRELVM v, SQInteger nformatstringidx, SQInteger* outlen, char** output) :param HSQUIRRELVM v: the target VM :param SQInteger nformatstringidx: index in the stack of the format string :param SQInteger* outlen: a pointer to an integer that will be filled with the length of the newly created string :param char** output: a pointer to a string pointer that will receive the newly created string :returns: an SQRESULT :remarks: the newly created string is allocated in the scratchpad memory. creates a new string formatted according to the object at position `nformatstringidx` and the optional parameters following it. The format string follows the same rules as the `printf` family of standard C functions( the "*" is not supported). ++++++++++++++++++ Regular Expessions ++++++++++++++++++ .. c:function:: SQRex* sqstd_rex_compile(SQAllocCtx alloc_ctx, const char *pattern, const char ** error) :param SQAllocCtx alloc_ctx VM memory allocation context handle :param char* pattern: a pointer to a zero terminated string containing the pattern that has to be compiled. :param char** error: a pointer to a string pointer that will be set with an error string in case of failure. :returns: a pointer to the compiled pattern compiles an expression and returns a pointer to the compiled version. in case of failure returns NULL.The returned object has to be deleted through the function sqstd_rex_free(). .. c:function:: void sqstd_rex_free(SQRex * exp) :param SQRex* exp: the expression structure that has to be deleted. deletes a expression structure created with sqstd_rex_compile() .. c:function:: SQBool sqstd_rex_match(SQRex * exp,const char * text) :param SQRex* exp: a compiled expression :param char* text: the string that has to be tested :returns: SQTrue if successful otherwise SQFalse returns SQTrue if the string specified in the parameter text is an exact match of the expression, otherwise returns SQFalse. .. c:function:: SQBool sqstd_rex_search(SQRex * exp, const char * text, const char ** out_begin, const char ** out_end) :param SQRex* exp: a compiled expression :param char* text: the string that has to be tested :param char** out_begin: a pointer to a string pointer that will be set with the beginning of the match :param char** out_end: a pointer to a string pointer that will be set with the end of the match :returns: SQTrue if successful otherwise SQFalse searches the first match of the expression in the string specified in the parameter text. if the match is found returns SQTrue and the sets out_begin to the beginning of the match and out_end at the end of the match; otherwise returns SQFalse. .. c:function:: SQBool sqstd_rex_searchrange(SQRex * exp, const char * text_begin, const char * text_end, const char ** out_begin, const char ** out_end) :param SQRex* exp: a compiled expression :param char* text_begin: a pointer to the beginnning of the string that has to be tested :param char* text_end: a pointer to the end of the string that has to be tested :param char** out_begin: a pointer to a string pointer that will be set with the beginning of the match :param char** out_end: a pointer to a string pointer that will be set with the end of the match :returns: SQTrue if successful otherwise SQFalse searches the first match of the expression in the string delimited by the parameter text_begin and text_end. if the match is found returns SQTrue and sets out_begin to the beginning of the match and out_end at the end of the match; otherwise returns SQFalse. .. c:function:: SQInteger sqstd_rex_getsubexpcount(SQRex * exp) :param SQRex* exp: a compiled expression :returns: the number of sub expressions matched by the expression returns the number of sub expressions matched by the expression .. c:function:: SQBool sqstd_rex_getsubexp(SQRex * exp, SQInteger n, SQRexMatch *subexp) :param SQRex* exp: a compiled expression :param SQInteger n: the index of the submatch(0 is the complete match) :param SQRexMatch* a: pointer to structure that will store the result :returns: the function returns SQTrue if n is a valid index; otherwise SQFalse. retrieve the begin and and pointer to the length of the sub expression indexed by n. The result is passed through the struct SQRexMatch. ================================================ FILE: doc/source/stdlib/stdsystemlib.rst ================================================ .. _stdlib_stdsystemlib: ================== The System library ================== The system library exposes operating system facilities like environment variables, process execution and file operations -------------- Quirrel API -------------- ++++++++++++++ Global Symbols ++++++++++++++ .. sq:function:: getenv(varaname) Returns a string containing the value of the environment variable `varname` .. sq:function:: remove(path) deletes the file specified by `path` .. sq:function:: rename(oldname, newname) renames the file or directory specified by `oldname` to the name given by `newname` .. sq:function:: system(cmd) executes the string `cmd` through the os command interpreter. -------------- C API -------------- .. _sqstd_register_systemlib: .. c:function:: SQRESULT sqstd_register_systemlib(HSQUIRRELVM v) :param HSQUIRRELVM v: the target VM :returns: an SQRESULT :remarks: The function aspects a table on top of the stack where to register the global library functions. initialize and register the system library in the given VM. ================================================ FILE: helpers/keyValueFile.h ================================================ #pragma once #include #include #include #include #include //file example: // // ; comment // include ../some_file.inc // boolean_value = yes ; comment // code = fn1(); fn2(); ; comment // // ; use '\' for concatination with the next line // multiline = line1 \ // line1 \ ; valid comment // line1 \ // line1 // // list = value1 ; call getValuesList("list") to get all these values in std::vector // list = value2 // list = value3 value4 value5 // list = value6 // // class KeyValueFile { public: typedef void (*PrintErrorFunc)(const char *); PrintErrorFunc printErrorFunc = nullptr; struct StringKeyValue { std::string key; std::string value; }; private: std::string fileName; std::vector kv; int includeDepth = 0; bool isUtf8Bom(const char * it) { return ( (unsigned char)(*it++) == 0xef && (unsigned char)(*it++) == 0xbb && (unsigned char)(*it) == 0xbf ); } bool isSpace(char c) const { return c == ' ' || c == '\t'; } std::string trimStr(const std::string & s) { int from = 0; int to = int(s.length()) - 1; while (to > 0 && isSpace(s[to])) to--; while (from <= to && isSpace(s[from])) from++; if (to < from) return std::string(""); return std::string(&(s[from]), to - from + 1); } void errorParam(const char * msg, const char * key_name) const { if (printErrorFunc) { std::string err = std::string("ERROR: ") + msg + ", at file '" + fileName + "', key '" + std::string(key_name) + "'\n"; printErrorFunc(err.c_str()); } } bool includeFile(const char * inc_file_name, int include_depth) { if (include_depth >= 16) { if (printErrorFunc) printErrorFunc( (std::string("ERROR: Maximum include depth exceeded on file '") + inc_file_name + "'\n").c_str()); return false; } int len = int(fileName.length()) - 1; while (len >= 0) { if (fileName[len] == '\\' || fileName[len] == '/') break; len--; } std::string combinedFileName(fileName.c_str(), len + 1); combinedFileName += inc_file_name; KeyValueFile kvFile; kvFile.includeDepth = include_depth + 1; kvFile.printErrorFunc = printErrorFunc; bool ok = kvFile.loadFromFile(combinedFileName.c_str()); if (ok) for (auto && value : kvFile.kv) kv.push_back(value); return ok; } public: bool loadFromFile(const char * file_name) { if (!file_name) return false; std::ifstream stream(file_name); if (stream.fail()) { if (printErrorFunc) printErrorFunc( (std::string("ERROR: Cannot open file '") + file_name + "'\n").c_str()); return false; } std::string buf((std::istreambuf_iterator(stream)), std::istreambuf_iterator()); return loadFromString(buf.c_str(), file_name); } bool loadFromString(const char * data, const char * debug_file_name = "") { fileName = debug_file_name; if (!data) return false; if (isUtf8Bom(data)) data += 3; int len = int(strlen(data)); StringKeyValue x; bool comment = false; bool readKey = true; bool eqFound = false; bool error = false; bool include = false; int line = 1; for (int i = 0; i <= len; i++) { char c = data[i]; if (c == '\\') { if (i == len - 1) i++; else { bool commentAfterConcat = false; for (int j = i + 1; j < len; j++) { if (data[j] == ';' && isSpace(data[j - 1])) commentAfterConcat = true; if (!commentAfterConcat && data[j] > ' ') break; bool concat = false; if (data[j] == 0x0d && data[j + 1] == 0x0a) { line++; i = j + 2; concat = true; } else if (data[j] == 0x0a) { line++; i = j + 1; concat = true; } if (concat) { if (comment) if (printErrorFunc) printErrorFunc((std::string("ERROR: Line concatenation inside comment. At file '") + fileName + "' line " + std::to_string(line - 1) + "\n").c_str()); if (data[i] > ' ' && data[i] != ';') if (printErrorFunc) printErrorFunc(( std::string("ERROR: The next line after the line concatenation symbol '\\' must be started with space. At file '") + fileName + "' line " + std::to_string(line) + "\n").c_str()); c = data[i]; break; } } } } if (c == 0x0d || c == 0x0a || c == 0) { x.key = trimStr(x.key); x.value = trimStr(x.value); if (!x.key.empty() && !x.value.empty() && eqFound && !include) { //printf("%s = \"%s\"\n", x.key.c_str(), x.value.c_str()); kv.push_back(x); } else if (!x.key.empty()) { if (include) { if (!debug_file_name || !debug_file_name[0]) { if (printErrorFunc) printErrorFunc( (std::string("ERROR: File name must be passed to loadFromString() when you are using 'include'. At file '") + fileName + "' line " + std::to_string(line) + "\n").c_str()); error = true; } else { bool ok = includeFile(x.value.c_str(), includeDepth); error |= !ok; } } else if (!eqFound) { if (printErrorFunc) printErrorFunc( (std::string("ERROR: Expected '=' at file '") + fileName + "' line " + std::to_string(line) + "\n").c_str()); error = true; } else if (x.value.empty()) { if (printErrorFunc) printErrorFunc( (std::string("ERROR: Expected value after '=' at file '") + fileName + "' line " + std::to_string(line) + "\n").c_str()); error = true; } } comment = false; readKey = true; eqFound = false; include = false; x.key.clear(); x.value.clear(); if (c == 0x0a) line++; } if (c == ';' && (i == 0 || isSpace(data[i - 1]) || data[i - 1] == 0x0a)) comment = true; if (comment) continue; if (readKey) { if (!isSpace(c) && c > ' ' && c != '=') x.key += c; else if (!x.key.empty()) { readKey = false; if (c == '=') eqFound = true; if (x.key == "include") include = true; } } else { if (c == '=' && !eqFound) eqFound = true; else if ((eqFound && c >= ' ') || include) x.value += c; } } return !error; } int count() const { return int(kv.size()); } const char * getStr(const char * key, const char * default_value = "") const { if (!key) return default_value; const char *res = default_value; for (int i = 0; i < int(kv.size()); i++) if (!strcmp(key, kv[i].key.c_str())) { if (res != default_value) errorParam("More than one key found", key); res = kv[i].value.c_str(); } return res; } bool getBool(const char * key, bool default_value) const { if (!key) return default_value; for (int i = 0; i < int(kv.size()); i++) if (!strcmp(key, kv[i].key.c_str())) { if (kv[i].value.empty()) errorParam("Value is empty", key); const char * v = kv[i].value.c_str(); bool isTrue = !strcmp(v, "1") || !strcmp(v, "yes") || !strcmp(v, "true"); bool isFalse = !strcmp(v, "0") || !strcmp(v, "no") || !strcmp(v, "false"); if (!isTrue && !isFalse) errorParam("Invalid boolean value, expected yes,true,1 or no,false,0", key); return isTrue; } return default_value; } std::vector getValuesList(const char * key) const // separated by spaces { std::vector res; for (auto && x : kv) if (!strcmp(key, x.key.c_str())) { const char * s = x.value.c_str(); int len = int(x.value.length()); int i = 0; while (i < len) { while (i < len && (isSpace(s[i]) || s[i] < ' ')) i++; int wordStart = i; while (i < len && s[i] > ' ') i++; if (i != wordStart) res.push_back(std::string(s + wordStart, i - wordStart)); } } return res; } const StringKeyValue & keyValue(int index) const { return kv[index]; } const std::string getFileName() const { return fileName; } }; ================================================ FILE: include/sq_char_class.h ================================================ #pragma once inline bool sq_isalnum(int c) { unsigned char uc = c; return (uc >= 'a' && uc <= 'z') || (uc >= 'A' && uc <= 'Z') || (uc >= '0' && uc <= '9'); } inline bool sq_isalpha(int c) { unsigned char uc = c; return (uc >= 'A' && uc <= 'Z') || (uc >= 'a' && uc <= 'z'); } inline bool sq_isblank(int c) { unsigned char uc = c; return uc == ' ' || uc == '\t'; } inline bool sq_iscntrl(int c) { unsigned char uc = c; return uc <= 31 || uc == 127; } inline bool sq_isdigit(int c) { unsigned char uc = c; return uc >= '0' && uc <= '9'; } inline bool sq_isgraph(int c) { unsigned char uc = c; return uc >= 33 && uc <= 126; } inline bool sq_islower(int c) { unsigned char uc = c; return uc >= 'a' && uc <= 'z'; } inline bool sq_isprint(int c) { unsigned char uc = c; return uc >= 32 && uc <= 126; } inline bool sq_ispunct(int c) { unsigned char uc = c; return (uc >= 33 && uc <= 47) || (uc >= 58 && uc <= 64) || (uc >= 91 && uc <= 96) || (uc >= 123 && uc <= 126); } inline bool sq_isspace(int c) { unsigned char uc = c; return uc == ' ' || uc == '\t' || uc == '\n' || uc == '\v' || uc == '\f' || uc == '\r'; } inline bool sq_isupper(int c) { unsigned char uc = c; return uc >= 'A' && uc <= 'Z'; } inline bool sq_isxdigit(int c) { unsigned char uc = c; return (uc >= '0' && uc <= '9') || (uc >= 'A' && uc <= 'F') || (uc >= 'a' && uc <= 'f'); } inline int sq_toupper(int c) { if (c < 'a' || c > 'z') return c; return c + 'A' - 'a'; } inline int sq_tolower(int c) { if (c < 'A' || c > 'Z') return c; return c + 'a' - 'A'; } ================================================ FILE: include/sqconfig.h ================================================ #ifndef _SQ64 #define _SQ64 // always use 64 bit Integers (even on 32 bit platform) #endif #define __STDC_FORMAT_MACROS // Linux/Adnroid won't define PRId* macroses without this #include #include #ifdef _SQ64 typedef int64_t SQInteger; typedef uint64_t SQUnsignedInteger; typedef uint64_t SQHash; /*should be the same size of a pointer*/ #else typedef intptr_t SQInteger; typedef uintptr_t SQUnsignedInteger; typedef uintptr_t SQHash; /*should be the same size of a pointer*/ #endif typedef int SQInt32; typedef unsigned int SQUnsignedInteger32; #ifdef SQUSEDOUBLE typedef double SQFloat; #else typedef float SQFloat; #endif #if defined(SQUSEDOUBLE) && !defined(_SQ64) || !defined(SQUSEDOUBLE) && defined(_SQ64) typedef int64_t SQRawObjectVal; //must be 64bits #define SQ_OBJECT_RAWINIT() { _unVal.raw = 0; } #else typedef SQUnsignedInteger SQRawObjectVal; //is 32 bits on 32 bits builds and 64 bits otherwise #define SQ_OBJECT_RAWINIT() #endif #ifndef SQ_ALIGNMENT // SQ_ALIGNMENT shall be less than or equal to SQ_MALLOC alignments, and its value shall be power of 2. #if defined(SQUSEDOUBLE) || defined(_SQ64) #define SQ_ALIGNMENT 8 #else #define SQ_ALIGNMENT 4 #endif #endif typedef void* SQUserPointer; typedef SQUnsignedInteger SQBool; typedef SQInteger SQRESULT; #if defined __EMSCRIPTEN__ #define scsprintf snprintf #elif _MSC_VER #define scsprintf _snprintf #else #define scsprintf snprintf #endif #ifdef _SQ64 #ifdef _MSC_VER #define scstrtol _strtoi64 #else #define scstrtol strtoll #endif #else #define scstrtol strtol #endif #ifdef _SQ64 #define _PRINT_INT_PREC "ll" #define _PRINT_INT_FMT "%" PRId64 #else #define _PRINT_INT_FMT "%d" #endif #define SQ_CHECK_THREAD_LEVEL_NONE 0 #define SQ_CHECK_THREAD_LEVEL_FAST 1 #define SQ_CHECK_THREAD_LEVEL_DEEP 2 #ifndef SQ_CHECK_THREAD #define SQ_CHECK_THREAD SQ_CHECK_THREAD_LEVEL_NONE #endif // doc strings and native function declaration strings #ifndef SQ_STORE_DOC_OBJECTS #define SQ_STORE_DOC_OBJECTS 1 #endif #define MIN_SQ_INTEGER SQInteger(1ULL << (sizeof(SQInteger) * 8 - 1)) ================================================ FILE: include/sqext.h ================================================ /* * Squirrel API extensions * Copyright (C) 2023 Gaijin Games KFT. All rights reserved */ #ifndef _SQEXT_H_ #define _SQEXT_H_ #include "squirrel.h" SQUIRREL_API SQRESULT sq_ext_getfuncinfo(HSQOBJECT obj, SQFunctionInfo *fi); SQUIRREL_API SQRESULT sq_ext_get_array_floats(HSQOBJECT obj, int start, int count, float * dest); SQUIRREL_API int sq_ext_get_array_int(HSQOBJECT obj, int index, int def = 0); SQUIRREL_API float sq_ext_get_array_float(HSQOBJECT obj, int index, float def = 0.f); #endif /*_SQEXT_H_*/ ================================================ FILE: include/sqio.h ================================================ #ifndef _SQASTIO_H_ #define _SQASTIO_H_ 1 #include #include #include #include "squirrel.h" class InputStream { protected: virtual uint8_t readByte() = 0; int64_t readVarint(); uint64_t readVaruint(); public: virtual ~InputStream() {} virtual size_t pos() = 0; virtual void seek(size_t) = 0; int8_t readInt8(); int16_t readInt16(); int32_t readInt32(); int64_t readInt64(); intptr_t readIntptr(); uint8_t readUInt8(); uint16_t readUInt16(); uint32_t readUInt32(); uint64_t readUInt64(); uintptr_t readUIntptr(); SQInteger readSQInteger() { #ifdef _SQ64 return readInt64(); #else return readIntptr(); #endif // _SQ64 } SQUnsignedInteger readSQUnsignedInteger() { #ifdef _SQ64 return readUInt64(); #else return readUIntptr(); #endif // _SQ64 } SQFloat readSQFloat() { #ifdef SQUSEDOUBLE int32_t t = readInt32(); return *(SQFloat *)&t; #else int64_t t = readInt64(); return *(SQFloat *)&t; #endif // SQUSEDOUBLE } uint64_t readRawUInt64(); }; class StdInputStream : public InputStream { std::istream &i; public: StdInputStream(std::istream &s) : i(s) {} uint8_t readByte(); size_t pos(); void seek(size_t); }; class FileInputStream : public InputStream { FILE *file; public: FileInputStream(const char *fileName); ~FileInputStream(); bool valid() const { return file != NULL; } uint8_t readByte(); size_t pos(); void seek(size_t); }; class MemoryInputStream : public InputStream { const uint8_t *buffer; const size_t size; size_t ptr; public: MemoryInputStream(const uint8_t *b, const size_t s) : buffer(b), size(s), ptr(0) {} uint8_t readByte(); size_t pos(); void seek(size_t); }; class OutputStream { protected: virtual void writeByte(uint8_t) = 0; void writeVarint(int64_t); void writeVaruint(uint64_t); public: virtual ~OutputStream() {} virtual size_t pos() = 0; virtual void seek(size_t pos) = 0; void writeInt8(int8_t); void writeInt16(int16_t); void writeInt32(int32_t); void writeInt64(int64_t); void writeIntptr(intptr_t); void writeUInt8(uint8_t); void writeUInt16(uint16_t); void writeUInt32(uint32_t); void writeUInt64(uint64_t); void writeUIntptr(uintptr_t); void writeSQInteger(SQInteger i) { #ifdef _SQ64 writeInt64(i); #else writeIntptr(i); #endif // _SQ64 } void writeSQUnsignedInteger(SQUnsignedInteger u) { #ifdef _SQ64 writeUInt64(u); #else writeUIntptr(u); #endif // _SQ64 } void writeSQFloat(SQFloat f) { #ifdef SQUSEDOUBLE writeInt32(*((int32_t*)&f)); #else writeInt64(*((int64_t*)&f)); #endif // SQUSEDOUBLE } void writeString(const char *s); void writeChar(char c); void writeRawUInt64(uint64_t v); }; class StdOutputStream : public OutputStream { std::ostream &o; public: StdOutputStream(std::ostream &s) : o(s) {} void writeByte(uint8_t); size_t pos(); void seek(size_t); }; class FileOutputStream : public OutputStream { FILE *file; bool close; public: FileOutputStream(FILE *f) : file(f), close(false) {} FileOutputStream(const char *filename); ~FileOutputStream(); bool valid() const { return file != NULL; } void writeByte(uint8_t); size_t pos(); void seek(size_t); }; class MemoryOutputStream : public OutputStream { uint8_t *_buffer; size_t _size; size_t ptr; void resize(size_t); public: MemoryOutputStream() : _buffer(NULL), _size(0), ptr(0) {} ~MemoryOutputStream(); void writeByte(uint8_t); size_t pos(); void seek(size_t); const uint8_t *buffer() const { return _buffer; } }; #endif // !_SQASTIO_H_ ================================================ FILE: include/sqstdaux.h ================================================ /* see copyright notice in squirrel.h */ #ifndef _SQSTD_AUXLIB_H_ #define _SQSTD_AUXLIB_H_ #ifdef __cplusplus extern "C" { #endif SQUIRREL_API void sqstd_seterrorhandlers(HSQUIRRELVM v); SQUIRREL_API void sqstd_printcallstack(HSQUIRRELVM v); SQUIRREL_API SQRESULT sqstd_formatcallstackstring(HSQUIRRELVM v); SQUIRREL_API SQRESULT sqstd_throwerrorf(HSQUIRRELVM v,const char *err,...); #ifdef __cplusplus } /*extern "C"*/ #endif #endif /* _SQSTD_AUXLIB_H_ */ ================================================ FILE: include/sqstdblob.h ================================================ /* see copyright notice in squirrel.h */ #ifndef _SQSTDBLOB_H_ #define _SQSTDBLOB_H_ #ifdef __cplusplus extern "C" { #endif SQUIRREL_API SQUserPointer sqstd_createblob(HSQUIRRELVM v, SQInteger size); SQUIRREL_API SQRESULT sqstd_getblob(HSQUIRRELVM v,SQInteger idx,SQUserPointer *ptr); SQUIRREL_API SQInteger sqstd_getblobsize(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sqstd_register_bloblib(HSQUIRRELVM v); #ifdef __cplusplus } /*extern "C"*/ #endif #endif /*_SQSTDBLOB_H_*/ ================================================ FILE: include/sqstddatetime.h ================================================ /* see copyright notice in squirrel.h */ #ifndef _SQSTD_DATETIMELIB_H_ #define _SQSTD_DATETIMELIB_H_ #ifdef __cplusplus extern "C" { #endif SQUIRREL_API SQRESULT sqstd_register_datetimelib(HSQUIRRELVM v); #ifdef __cplusplus } /*extern "C"*/ #endif #endif /* _SQSTD_DATETIMELIB_H_ */ ================================================ FILE: include/sqstddebug.h ================================================ /* see copyright notice in squirrel.h */ #ifndef _SQSTD_DEBUG_H_ #define _SQSTD_DEBUG_H_ #ifdef __cplusplus extern "C" { #endif SQUIRREL_API SQRESULT sqstd_register_debuglib(HSQUIRRELVM v); #ifdef __cplusplus } /*extern "C"*/ #endif #endif // _SQSTD_DEBUG_H_ ================================================ FILE: include/sqstdio.h ================================================ /* see copyright notice in squirrel.h */ #ifndef _SQSTDIO_H_ #define _SQSTDIO_H_ #ifdef __cplusplus #define SQSTD_STREAM_TYPE_TAG 0x80000000 struct SQStream { SQStream() = default; SQStream(const SQStream&) = default; SQStream(SQStream&&) = default; SQStream& operator=(const SQStream&) = default; SQStream& operator=(SQStream&&) = default; virtual ~SQStream() {} virtual SQInteger Read(void *buffer, SQInteger size) = 0; virtual SQInteger Write(const void *buffer, SQInteger size) = 0; virtual SQInteger Flush() = 0; virtual SQInteger Tell() = 0; virtual SQInteger Len() = 0; virtual SQInteger Seek(SQInteger offset, SQInteger origin) = 0; virtual bool IsValid() = 0; virtual bool EOS() = 0; }; extern "C" { #endif #define SQ_SEEK_CUR 0 #define SQ_SEEK_END 1 #define SQ_SEEK_SET 2 typedef void* SQFILE; SQUIRREL_API SQFILE sqstd_fopen(const char *,const char *); SQUIRREL_API SQInteger sqstd_fread(SQUserPointer, SQInteger, SQInteger, SQFILE); SQUIRREL_API SQInteger sqstd_fwrite(const SQUserPointer, SQInteger, SQInteger, SQFILE); SQUIRREL_API SQInteger sqstd_fseek(SQFILE , SQInteger , SQInteger); SQUIRREL_API SQInteger sqstd_ftell(SQFILE); SQUIRREL_API SQInteger sqstd_fflush(SQFILE); SQUIRREL_API SQInteger sqstd_fclose(SQFILE); SQUIRREL_API SQInteger sqstd_feof(SQFILE); SQUIRREL_API SQRESULT sqstd_createfile(HSQUIRRELVM v, SQFILE file,SQBool own); SQUIRREL_API SQRESULT sqstd_getfile(HSQUIRRELVM v, SQInteger idx, SQFILE *file); //compiler helpers SQUIRREL_API SQRESULT sqstd_loadfile(HSQUIRRELVM v,const char *filename,SQBool printerror); SQUIRREL_API SQRESULT sqstd_dofile(HSQUIRRELVM v,const char *filename,SQBool retval,SQBool printerror); SQUIRREL_API SQRESULT sqstd_writeclosuretofile(HSQUIRRELVM v,const char *filename); SQUIRREL_API SQRESULT sqstd_init_streamclass(HSQUIRRELVM v); SQUIRREL_API SQRESULT sqstd_register_iolib(HSQUIRRELVM v); #ifdef __cplusplus } /*extern "C"*/ #endif #endif /*_SQSTDIO_H_*/ ================================================ FILE: include/sqstdmath.h ================================================ /* see copyright notice in squirrel.h */ #ifndef _SQSTD_MATH_H_ #define _SQSTD_MATH_H_ #ifdef __cplusplus extern "C" { #endif SQUIRREL_API SQRESULT sqstd_register_mathlib(HSQUIRRELVM v); #ifdef __cplusplus } /*extern "C"*/ #endif #endif /*_SQSTD_MATH_H_*/ ================================================ FILE: include/sqstdstring.h ================================================ /* see copyright notice in squirrel.h */ #ifndef _SQSTD_STRING_H_ #define _SQSTD_STRING_H_ #ifdef __cplusplus extern "C" { #endif typedef unsigned int SQRexBool; typedef struct SQRex SQRex; typedef struct SQAllocContextT * SQAllocContext; typedef struct { const char *begin; SQInteger len; } SQRexMatch; SQUIRREL_API SQRex *sqstd_rex_compile(SQAllocContext ctx, const char *pattern,const char **error); SQUIRREL_API void sqstd_rex_free(SQRex *exp); SQUIRREL_API SQBool sqstd_rex_match(SQRex* exp,const char* text); SQUIRREL_API SQBool sqstd_rex_search(SQRex* exp,const char* text, const char** out_begin, const char** out_end); SQUIRREL_API SQBool sqstd_rex_searchrange(SQRex* exp,const char* text_begin,const char* text_end,const char** out_begin, const char** out_end); SQUIRREL_API SQInteger sqstd_rex_getsubexpcount(SQRex* exp); SQUIRREL_API SQBool sqstd_rex_getsubexp(SQRex* exp, SQInteger n, SQRexMatch *subexp); SQUIRREL_API SQRESULT sqstd_format(HSQUIRRELVM v,SQInteger nformatstringidx,SQInteger *outlen,char **output); SQUIRREL_API void sqstd_pushstringf(HSQUIRRELVM v,const char *s,...); SQUIRREL_API SQRESULT sqstd_register_stringlib(HSQUIRRELVM v); #ifdef __cplusplus } /*extern "C"*/ #endif #endif /*_SQSTD_STRING_H_*/ ================================================ FILE: include/sqstdsystem.h ================================================ /* see copyright notice in squirrel.h */ #ifndef _SQSTD_SYSTEMLIB_H_ #define _SQSTD_SYSTEMLIB_H_ #ifdef __cplusplus extern "C" { #endif SQUIRREL_API SQRESULT sqstd_register_command_line_args(HSQUIRRELVM v, int argc, char ** argv); SQUIRREL_API SQRESULT sqstd_register_systemlib(HSQUIRRELVM v); #ifdef __cplusplus } /*extern "C"*/ #endif #endif /* _SQSTD_SYSTEMLIB_H_ */ ================================================ FILE: include/squirrel.h ================================================ /* Copyright (c) 2003-2017 Alberto Demichelis Copyright (c) 2016-2023 by Gaijin Games KFT Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #ifndef _SQUIRREL_H_ #define _SQUIRREL_H_ #ifdef _SQ_CONFIG_INCLUDE #include _SQ_CONFIG_INCLUDE #endif #ifdef __cplusplus extern "C" { #else #include #endif #include // memset #ifndef SQUIRREL_API #define SQUIRREL_API extern #endif #if (defined(_WIN64) || defined(_LP64)) #ifndef _SQ64 #define _SQ64 #endif #endif #define SQTrue (1) #define SQFalse (0) struct SQVM; struct SQTable; struct SQArray; struct SQString; struct SQClosure; struct SQGenerator; struct SQNativeClosure; struct SQUserData; struct SQFunctionProto; struct SQRefCounted; struct SQClass; struct SQInstance; struct SQDelegable; struct SQOuter; class OutputStream; class Arena; class KeyValueFile; namespace SQCompilation { struct SqASTData; } #include "sqconfig.h" #include #define SQUIRREL_VERSION_NUMBER_MAJOR 4 #define SQUIRREL_VERSION_NUMBER_MINOR 20 #define SQUIRREL_VERSION_NUMBER_PATCH 0 #define SQ_STRINGIFY_HELPER(x) #x #define SQ_STRINGIFY(x) SQ_STRINGIFY_HELPER(x) #define SQUIRREL_VERSION \ SQ_STRINGIFY(SQUIRREL_VERSION_NUMBER_MAJOR) "." \ SQ_STRINGIFY(SQUIRREL_VERSION_NUMBER_MINOR) "." \ SQ_STRINGIFY(SQUIRREL_VERSION_NUMBER_PATCH) #define SQUIRREL_COPYRIGHT "Copyright (C) 2003-2016 Alberto Demichelis; 2016-2025 Gaijin Games KFT" #define SQ_VMSTATE_IDLE 0 #define SQ_VMSTATE_RUNNING 1 #define SQ_VMSTATE_SUSPENDED 2 #define SQUIRREL_EOB 0 #define SQ_BYTECODE_STREAM_TAG 0xFAFA #define SQOBJECT_REF_COUNTED 0x08000000 #define SQOBJECT_NUMERIC 0x04000000 #define SQOBJECT_DELEGABLE 0x02000000 #define SQOBJ_FLAG_IMMUTABLE 0x01 #define SQ_MATCHTYPEMASKSTRING (-99999) #define _RT_MASK 0x00FFFFFF #define _RAW_TYPE(type) ((type)&_RT_MASK) #define _RT_NULL 0x00000001 #define _RT_INTEGER 0x00000002 #define _RT_FLOAT 0x00000004 #define _RT_BOOL 0x00000008 #define _RT_STRING 0x00000010 #define _RT_TABLE 0x00000020 #define _RT_ARRAY 0x00000040 #define _RT_USERDATA 0x00000080 #define _RT_CLOSURE 0x00000100 #define _RT_NATIVECLOSURE 0x00000200 #define _RT_GENERATOR 0x00000400 #define _RT_USERPOINTER 0x00000800 #define _RT_THREAD 0x00001000 #define _RT_FUNCPROTO 0x00002000 #define _RT_CLASS 0x00004000 #define _RT_INSTANCE 0x00008000 #define _RT_WEAKREF 0x00010000 #define _RT_OUTER 0x00020000 #define _RT_FREE_TABLE_SLOT 0x80000000 typedef enum tagSQObjectType{ OT_NULL = 0, // Note: checking typemask for _RT_NULL is non-obvious and is implemented in a special manner OT_INTEGER = (_RT_INTEGER|SQOBJECT_NUMERIC), OT_FLOAT = (_RT_FLOAT|SQOBJECT_NUMERIC), OT_BOOL = (_RT_BOOL), OT_STRING = (_RT_STRING|SQOBJECT_REF_COUNTED), OT_TABLE = (_RT_TABLE|SQOBJECT_REF_COUNTED|SQOBJECT_DELEGABLE), OT_ARRAY = (_RT_ARRAY|SQOBJECT_REF_COUNTED), OT_USERDATA = (_RT_USERDATA|SQOBJECT_REF_COUNTED|SQOBJECT_DELEGABLE), OT_CLOSURE = (_RT_CLOSURE|SQOBJECT_REF_COUNTED), OT_NATIVECLOSURE = (_RT_NATIVECLOSURE|SQOBJECT_REF_COUNTED), OT_GENERATOR = (_RT_GENERATOR|SQOBJECT_REF_COUNTED), OT_USERPOINTER = _RT_USERPOINTER, OT_THREAD = (_RT_THREAD|SQOBJECT_REF_COUNTED) , OT_FUNCPROTO = (_RT_FUNCPROTO|SQOBJECT_REF_COUNTED), //internal usage only OT_CLASS = (_RT_CLASS|SQOBJECT_REF_COUNTED), OT_INSTANCE = (_RT_INSTANCE|SQOBJECT_REF_COUNTED|SQOBJECT_DELEGABLE), OT_WEAKREF = (_RT_WEAKREF|SQOBJECT_REF_COUNTED), OT_OUTER = (_RT_OUTER|SQOBJECT_REF_COUNTED), //internal usage only OT_FREE_TABLE_SLOT = _RT_FREE_TABLE_SLOT //internal usage only }SQObjectType; typedef uint8_t SQObjectFlags; #define ISREFCOUNTED(t) ((t)&SQOBJECT_REF_COUNTED) typedef union tagSQObjectValue { struct SQTable *pTable; struct SQArray *pArray; struct SQClosure *pClosure; struct SQOuter *pOuter; struct SQGenerator *pGenerator; struct SQNativeClosure *pNativeClosure; struct SQString *pString; struct SQUserData *pUserData; SQInteger nInteger; SQFloat fFloat; SQUserPointer pUserPointer; struct SQFunctionProto *pFunctionProto; struct SQRefCounted *pRefCounted; struct SQDelegable *pDelegable; struct SQVM *pThread; struct SQClass *pClass; struct SQInstance *pInstance; struct SQWeakRef *pWeakRef; SQRawObjectVal raw; }SQObjectValue; typedef struct tagSQObject { SQObjectType _type; SQObjectFlags _flags; SQObjectValue _unVal; }SQObject; typedef struct tagSQMemberHandle{ SQInteger _index; uint8_t _static; uint8_t _isNativeField; }SQMemberHandle; typedef struct tagSQStackInfos{ const char* funcname; const char* source; SQInteger line; }SQStackInfos; typedef enum tagSQMessageSeverity{ SEV_HINT, SEV_WARNING, SEV_ERROR, }SQMessageSeverity; typedef struct SQVM* HSQUIRRELVM; typedef SQObject HSQOBJECT; typedef SQMemberHandle HSQMEMBERHANDLE; typedef SQInteger (*SQFUNCTION)(HSQUIRRELVM); typedef SQInteger (*SQRELEASEHOOK)(HSQUIRRELVM vm,SQUserPointer,SQInteger size); typedef void (*SQCOMPILERERROR)(HSQUIRRELVM,SQMessageSeverity /*severity*/,const char * /*desc*/,const char * /*source*/,SQInteger /*line*/,SQInteger /*column*/, const char * /*extra info*/); typedef void (*SQPRINTFUNCTION)(HSQUIRRELVM,const char * ,...); typedef void (*SQDEBUGHOOK)(HSQUIRRELVM /*v*/, SQInteger /*type*/, const char * /*sourcename*/, SQInteger /*line*/, const char * /*funcname*/); typedef void (*SQCOMPILELINEHOOK)(HSQUIRRELVM /*v*/, const char * /*sourcename*/, SQInteger /*line*/); typedef SQInteger (*SQWRITEFUNC)(SQUserPointer,SQUserPointer,SQInteger); typedef SQInteger (*SQREADFUNC)(SQUserPointer,SQUserPointer,SQInteger); typedef SQInteger (*SQGETTHREAD)(); typedef void (*SQSQCALLHOOK)(HSQUIRRELVM); typedef bool (*SQWATCHDOGHOOK)(HSQUIRRELVM, bool kick); typedef SQInteger (*SQLEXREADFUNC)(SQUserPointer); typedef struct tagSQRegFunction{ const char *name; SQFUNCTION f; SQInteger nparamscheck; const char *typemask; const char *docstring; bool pure; bool nodiscard; }SQRegFunction; typedef struct tagSQRegFunctionFromStr{ SQFUNCTION f; const char *declstring; const char *docstring; }SQRegFunctionFromStr; typedef struct tagSQFunctionInfo { SQUserPointer funcid; const char *name; const char *source; SQInteger line; }SQFunctionInfo; #define BIT(n) (1ULL << (n)) enum CompilationOptions : SQUnsignedInteger { CO_CLOSURE_HOISTING_OPT = BIT(1) }; #undef BIT typedef struct tagSQCompilerMessage { int intId; const char* textId; int line; int column; int columnsWidth; const char* message; const char* fileName; bool isError; } SQCompilerMessage; typedef void (*SQ_COMPILER_DIAG_CB)(HSQUIRRELVM v, const SQCompilerMessage *msg); typedef struct tagSQModuleImportSlot { const char *name; const char *alias; // null if not specified int line; int column; } SQModuleImportSlot; typedef struct tagSQModuleImport { const char *name; const char *alias; // null if not specified int numSlots; SQModuleImportSlot *slots; int line; int nameColumn; int aliasColumn; } SQModuleImport; /*vm*/ SQUIRREL_API HSQUIRRELVM sq_open(SQInteger initialstacksize); SQUIRREL_API HSQUIRRELVM sq_newthread(HSQUIRRELVM friendvm, SQInteger initialstacksize); SQUIRREL_API void sq_seterrorhandler(HSQUIRRELVM v); SQUIRREL_API HSQOBJECT sq_geterrorhandler(HSQUIRRELVM v); SQUIRREL_API void sq_close(HSQUIRRELVM v); SQUIRREL_API void sq_setforeignptr(HSQUIRRELVM v,SQUserPointer p); SQUIRREL_API SQUserPointer sq_getforeignptr(HSQUIRRELVM v); SQUIRREL_API void sq_setsharedforeignptr(HSQUIRRELVM v,SQUserPointer p); SQUIRREL_API SQUserPointer sq_getsharedforeignptr(HSQUIRRELVM v); SQUIRREL_API void sq_setvmreleasehook(HSQUIRRELVM v,SQRELEASEHOOK hook); SQUIRREL_API SQRELEASEHOOK sq_getvmreleasehook(HSQUIRRELVM v); SQUIRREL_API void sq_setsharedreleasehook(HSQUIRRELVM v,SQRELEASEHOOK hook); SQUIRREL_API SQRELEASEHOOK sq_getsharedreleasehook(HSQUIRRELVM v); SQUIRREL_API void sq_setprintfunc(HSQUIRRELVM v, SQPRINTFUNCTION printfunc, SQPRINTFUNCTION errfunc); SQUIRREL_API SQPRINTFUNCTION sq_getprintfunc(HSQUIRRELVM v); SQUIRREL_API SQPRINTFUNCTION sq_geterrorfunc(HSQUIRRELVM v); SQUIRREL_API SQRESULT sq_suspendvm(HSQUIRRELVM v); SQUIRREL_API SQRESULT sq_wakeupvm(HSQUIRRELVM v,SQBool resumedret,SQBool retval,SQBool invoke_err_handler,SQBool throwerror); SQUIRREL_API SQInteger sq_getvmstate(HSQUIRRELVM v); SQUIRREL_API SQRESULT sq_registerbaselib(HSQUIRRELVM v); SQUIRREL_API SQRESULT sq_registertypeslib(HSQUIRRELVM v); /*compiler*/ SQUIRREL_API SQRESULT sq_compile(HSQUIRRELVM v, const char *s, SQInteger size, const char *sourcename, SQBool raiseerror, const HSQOBJECT *bindings = nullptr); SQUIRREL_API SQCompilation::SqASTData *sq_parsetoast(HSQUIRRELVM v, const char *s, SQInteger size, const char *sourcename, SQBool preserveComments, SQBool raiseerror); SQUIRREL_API SQRESULT sq_translateasttobytecode(HSQUIRRELVM v, SQCompilation::SqASTData *astData, const HSQOBJECT *bindings, const char *s, SQInteger size, SQBool raiseerror); SQUIRREL_API void sq_analyzeast(HSQUIRRELVM v, SQCompilation::SqASTData *astData, const HSQOBJECT *bindings, const char *s, SQInteger size); SQUIRREL_API void sq_checktrailingspaces(HSQUIRRELVM v, const char *sourceName, const char *s, SQInteger size); SQUIRREL_API SQRESULT sq_getimports(HSQUIRRELVM v, SQCompilation::SqASTData *astData, SQInteger *num, SQModuleImport **imports); SQUIRREL_API void sq_freeimports(HSQUIRRELVM v, SQInteger num, SQModuleImport *imports); SQUIRREL_API void sq_dumpast(HSQUIRRELVM v, SQCompilation::SqASTData *astData, bool nodesLocation, OutputStream *s); SQUIRREL_API void sq_dumpbytecode(HSQUIRRELVM v, HSQOBJECT obj, OutputStream *s, int instruction_index = -1); SQUIRREL_API void sq_reset_static_memos(HSQUIRRELVM v, HSQOBJECT func); SQUIRREL_API SQCompilation::SqASTData *sq_allocateASTData(HSQUIRRELVM v); SQUIRREL_API void sq_releaseASTData(HSQUIRRELVM v, SQCompilation::SqASTData *astData); SQUIRREL_API void sq_setcompilationoption(HSQUIRRELVM v, enum CompilationOptions co, bool value); SQUIRREL_API bool sq_checkcompilationoption(HSQUIRRELVM v, enum CompilationOptions co); SQUIRREL_API void sq_enablevartrace(HSQUIRRELVM v, SQBool enable); SQUIRREL_API SQBool sq_isvartracesupported(); SQUIRREL_API void sq_lineinfo_in_expressions(HSQUIRRELVM v, SQBool enable); SQUIRREL_API void sq_notifyallexceptions(HSQUIRRELVM v, SQBool enable); SQUIRREL_API void sq_setcompilererrorhandler(HSQUIRRELVM v,SQCOMPILERERROR f); SQUIRREL_API void sq_setcompilerdiaghandler(HSQUIRRELVM v, SQ_COMPILER_DIAG_CB f); SQUIRREL_API SQCOMPILERERROR sq_getcompilererrorhandler(HSQUIRRELVM v); /*stack operations*/ SQUIRREL_API void sq_push(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API void sq_pop(HSQUIRRELVM v,SQInteger nelemstopop); SQUIRREL_API void sq_poptop(HSQUIRRELVM v); SQUIRREL_API void sq_remove(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQInteger sq_gettop(HSQUIRRELVM v); SQUIRREL_API void sq_settop(HSQUIRRELVM v,SQInteger newtop); SQUIRREL_API SQRESULT sq_reservestack(HSQUIRRELVM v,SQInteger nsize); SQUIRREL_API SQInteger sq_cmp(HSQUIRRELVM v); SQUIRREL_API bool sq_cmpraw(HSQUIRRELVM v, HSQOBJECT &lhs, HSQOBJECT &rhs, SQInteger &res); SQUIRREL_API void sq_move(HSQUIRRELVM dest,HSQUIRRELVM src,SQInteger idx); /*object creation handling*/ SQUIRREL_API SQUserPointer sq_newuserdata(HSQUIRRELVM v,SQUnsignedInteger size); SQUIRREL_API void sq_newtable(HSQUIRRELVM v); SQUIRREL_API void sq_newtableex(HSQUIRRELVM v,SQInteger initialcapacity); SQUIRREL_API void sq_newarray(HSQUIRRELVM v,SQInteger size); SQUIRREL_API SQRESULT sq_new_closure_slot_from_decl_string(HSQUIRRELVM v, SQFUNCTION func, SQUnsignedInteger nfreevars, const char *function_decl, const char *docstring); SQUIRREL_API void sq_newclosure(HSQUIRRELVM v,SQFUNCTION func,SQUnsignedInteger nfreevars); SQUIRREL_API SQRESULT sq_setparamscheck(HSQUIRRELVM v,SQInteger nparamscheck,const char *typemask); SQUIRREL_API SQRESULT sq_bindenv(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API void sq_pushstring(HSQUIRRELVM v,const char *s,SQInteger len); SQUIRREL_API void sq_pushfloat(HSQUIRRELVM v,SQFloat f); SQUIRREL_API void sq_pushinteger(HSQUIRRELVM v,SQInteger n); SQUIRREL_API void sq_pushbool(HSQUIRRELVM v,SQBool b); SQUIRREL_API void sq_pushuserpointer(HSQUIRRELVM v,SQUserPointer p); SQUIRREL_API void sq_pushnull(HSQUIRRELVM v); SQUIRREL_API void sq_pushthread(HSQUIRRELVM v, HSQUIRRELVM thread); SQUIRREL_API SQObjectType sq_gettype(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_typeof(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQInteger sq_getsize(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQHash sq_gethash(HSQUIRRELVM v, SQInteger idx); SQUIRREL_API SQRESULT sq_getbase(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQBool sq_instanceof(HSQUIRRELVM v); SQUIRREL_API SQRESULT sq_tostring(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API void sq_tobool(HSQUIRRELVM v, SQInteger idx, SQBool *b); SQUIRREL_API SQRESULT sq_getstringandsize(HSQUIRRELVM v,SQInteger idx,const char **c,SQInteger *size); SQUIRREL_API SQRESULT sq_getstring(HSQUIRRELVM v,SQInteger idx,const char **c); SQUIRREL_API SQRESULT sq_getinteger(HSQUIRRELVM v,SQInteger idx,SQInteger *i); SQUIRREL_API SQRESULT sq_getfloat(HSQUIRRELVM v,SQInteger idx,SQFloat *f); SQUIRREL_API SQRESULT sq_getbool(HSQUIRRELVM v,SQInteger idx,SQBool *b); SQUIRREL_API SQRESULT sq_getthread(HSQUIRRELVM v,SQInteger idx,HSQUIRRELVM *thread); SQUIRREL_API SQRESULT sq_getuserpointer(HSQUIRRELVM v,SQInteger idx,SQUserPointer *p); SQUIRREL_API SQRESULT sq_getuserdata(HSQUIRRELVM v,SQInteger idx,SQUserPointer *p,SQUserPointer *typetag); SQUIRREL_API SQRESULT sq_settypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer typetag); SQUIRREL_API SQRESULT sq_gettypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer *typetag); SQUIRREL_API void sq_setreleasehook(HSQUIRRELVM v,SQInteger idx,SQRELEASEHOOK hook); SQUIRREL_API SQRELEASEHOOK sq_getreleasehook(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API char *sq_getscratchpad(HSQUIRRELVM v,SQInteger minsize); SQUIRREL_API SQRESULT sq_getfunctioninfo(HSQUIRRELVM v,SQInteger level,SQFunctionInfo *fi); SQUIRREL_API SQRESULT sq_getclosureinfo(HSQUIRRELVM v,SQInteger idx,SQInteger *nparams,SQInteger *nfreevars); SQUIRREL_API SQRESULT sq_getclosurename(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_setnativeclosurename(HSQUIRRELVM v,SQInteger idx,const char *name); SQUIRREL_API SQRESULT sq_setnativeclosuredocstring(HSQUIRRELVM v,SQInteger idx,const char *docstring); SQUIRREL_API SQRESULT sq_setobjectdocstring(HSQUIRRELVM v, const HSQOBJECT *obj, const char *docstring); SQUIRREL_API SQRESULT sq_setinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer p); SQUIRREL_API SQRESULT sq_getinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer *p,SQUserPointer typetag); SQUIRREL_API SQRESULT sq_setclassudsize(HSQUIRRELVM v, SQInteger idx, SQInteger udsize); SQUIRREL_API SQRESULT sq_newclass(HSQUIRRELVM v,SQBool hasbase); SQUIRREL_API SQRESULT sq_createinstance(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_getclass(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API void sq_weakref(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_getmemberhandle(HSQUIRRELVM v,SQInteger idx,HSQMEMBERHANDLE *handle); SQUIRREL_API SQRESULT sq_getbyhandle(HSQUIRRELVM v,SQInteger idx,const HSQMEMBERHANDLE *handle); SQUIRREL_API SQRESULT sq_setbyhandle(HSQUIRRELVM v,SQInteger idx,const HSQMEMBERHANDLE *handle); /*native fields: direct access to C++ struct fields in inline userdata*/ #define SQNFT_FLOAT32 0 #define SQNFT_FLOAT64 1 #define SQNFT_INT32 2 #define SQNFT_INT64 3 #define SQNFT_BOOL 4 SQUIRREL_API SQRESULT sq_registernativefield(HSQUIRRELVM v, SQInteger classidx, const char *name, SQInteger offset, SQInteger fieldtype); /*object manipulation*/ SQUIRREL_API void sq_pushroottable(HSQUIRRELVM v); SQUIRREL_API void sq_pushregistrytable(HSQUIRRELVM v); SQUIRREL_API void sq_pushconsttable(HSQUIRRELVM v); SQUIRREL_API SQRESULT sq_setroottable(HSQUIRRELVM v); SQUIRREL_API SQRESULT sq_setconsttable(HSQUIRRELVM v); SQUIRREL_API SQRESULT sq_newslot(HSQUIRRELVM v, SQInteger idx, SQBool bstatic); //-V1071 SQUIRREL_API SQRESULT sq_deleteslot(HSQUIRRELVM v,SQInteger idx,SQBool pushval); //-V1071 SQUIRREL_API SQRESULT sq_set(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_get(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_rawget(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_rawset(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_rawdeleteslot(HSQUIRRELVM v,SQInteger idx,SQBool pushval); SQUIRREL_API SQRESULT sq_newmember(HSQUIRRELVM v,SQInteger idx,SQBool bstatic); SQUIRREL_API SQRESULT sq_arrayappend(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_arraypop(HSQUIRRELVM v,SQInteger idx,SQBool pushval); SQUIRREL_API SQRESULT sq_arrayresize(HSQUIRRELVM v,SQInteger idx,SQInteger newsize); SQUIRREL_API SQRESULT sq_arrayreverse(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_arrayremove(HSQUIRRELVM v,SQInteger idx,SQInteger itemidx); SQUIRREL_API SQRESULT sq_arrayinsert(HSQUIRRELVM v,SQInteger idx,SQInteger destpos); SQUIRREL_API SQRESULT sq_setdelegate(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_getdelegate(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_clone(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_setfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval); SQUIRREL_API SQRESULT sq_next(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_getweakrefval(HSQUIRRELVM v,SQInteger idx); SQUIRREL_API SQRESULT sq_clear(HSQUIRRELVM v,SQInteger idx,SQBool freemem = SQTrue); SQUIRREL_API SQRESULT sq_freeze(HSQUIRRELVM v, SQInteger idx); SQUIRREL_API SQRESULT sq_freeze_inplace(HSQUIRRELVM v, SQInteger idx); SQUIRREL_API SQRESULT sq_mark_pure_inplace(HSQUIRRELVM v, SQInteger idx); SQUIRREL_API bool sq_is_pure_function(HSQOBJECT *func); /*calls*/ SQUIRREL_API SQRESULT sq_call(HSQUIRRELVM v,SQInteger params,SQBool retval,SQBool invoke_err_handler); SQUIRREL_API SQRESULT sq_resume(HSQUIRRELVM v,SQBool retval,SQBool invoke_err_handler); SQUIRREL_API const char *sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedInteger idx); SQUIRREL_API SQRESULT sq_getcallee(HSQUIRRELVM v); SQUIRREL_API const char *sq_getfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval); SQUIRREL_API void sq_throwparamtypeerror(HSQUIRRELVM v, SQInteger nparam, SQInteger typemask, SQInteger type); SQUIRREL_API SQRESULT sq_throwerror(HSQUIRRELVM v,const char *err); // see also: sqstd_throwerrorf(HSQUIRRELVM v,const char *err,...) SQUIRREL_API SQRESULT sq_throwobject(HSQUIRRELVM v); SQUIRREL_API void sq_reseterror(HSQUIRRELVM v); SQUIRREL_API void sq_getlasterror(HSQUIRRELVM v); SQUIRREL_API SQRESULT sq_tailcall(HSQUIRRELVM v, SQInteger nparams); /*raw object handling*/ SQUIRREL_API SQRESULT sq_getstackobj(HSQUIRRELVM v,SQInteger idx,HSQOBJECT *po);//-V1071 SQUIRREL_API void sq_pushobj(HSQUIRRELVM v,const HSQOBJECT *po); #ifdef __cplusplus static inline void sq_pushobject(HSQUIRRELVM v, const HSQOBJECT &o) { return sq_pushobj(v, &o); } #else static inline void sq_pushobject(HSQUIRRELVM v, HSQOBJECT o) { return sq_pushobj(v, &o); } #endif SQUIRREL_API void sq_addref_refcounted(HSQUIRRELVM v,HSQOBJECT *po); SQUIRREL_API SQBool sq_release_refcounted(HSQUIRRELVM v,HSQOBJECT *po); SQUIRREL_API SQUnsignedInteger sq_getrefcount(HSQUIRRELVM v,HSQOBJECT *po); static inline void sq_resetobject(HSQOBJECT *po) { #ifdef __cplusplus static_assert((int)OT_NULL == 0); #endif memset(po, 0, sizeof(*po)); } SQUIRREL_API const char *sq_objtostring(const HSQOBJECT *o); SQUIRREL_API SQBool sq_objtobool(const HSQOBJECT *o); SQUIRREL_API SQBool sq_obj_is_true(const HSQOBJECT *o); SQUIRREL_API SQInteger sq_objtointeger(const HSQOBJECT *o); SQUIRREL_API SQFloat sq_objtofloat(const HSQOBJECT *o); SQUIRREL_API SQUserPointer sq_objtouserpointer(const HSQOBJECT *o); SQUIRREL_API SQRESULT sq_getobjtypetag(const HSQOBJECT *o,SQUserPointer * typetag); SQUIRREL_API SQUnsignedInteger sq_getvmrefcount(HSQUIRRELVM v, const HSQOBJECT *po); SQUIRREL_API const char* sq_objtypestr(SQObjectType tp); SQUIRREL_API SQRESULT sq_obj_get(HSQUIRRELVM v, const HSQOBJECT *obj, const HSQOBJECT *slot, HSQOBJECT *out, bool raw); SQUIRREL_API SQBool sq_obj_cmp(HSQUIRRELVM v, const HSQOBJECT *a, const HSQOBJECT *b, SQInteger *res); SQUIRREL_API bool sq_obj_is_equal(HSQUIRRELVM v, const HSQOBJECT *a, const HSQOBJECT *b); SQUIRREL_API bool sq_fast_equal_by_value_deep(const HSQOBJECT *a, const HSQOBJECT *b, int depth); SQUIRREL_API SQRESULT sq_obj_getuserdata(const HSQOBJECT *obj, SQUserPointer *p, SQUserPointer *typetag); SQUIRREL_API SQInteger sq_obj_getsize(const HSQOBJECT *obj); SQUIRREL_API SQRESULT sq_obj_getinstanceup(const HSQOBJECT *obj, SQUserPointer *p, SQUserPointer typetag); SQUIRREL_API SQRESULT sq_obj_set(HSQUIRRELVM v, const HSQOBJECT *obj, const HSQOBJECT *key, const HSQOBJECT *val, bool raw); SQUIRREL_API SQRESULT sq_obj_newslot(HSQUIRRELVM v, const HSQOBJECT *obj, const HSQOBJECT *key, const HSQOBJECT *val, bool bstatic); SQUIRREL_API void sq_getregistrytableobj(HSQUIRRELVM v, HSQOBJECT *out); SQUIRREL_API SQBool sq_tracevar(HSQUIRRELVM v, const HSQOBJECT * container, const HSQOBJECT * key, char * buf, int buf_size); /*GC*/ SQUIRREL_API SQInteger sq_collectgarbage(HSQUIRRELVM v); SQUIRREL_API SQRESULT sq_resurrectunreachable(HSQUIRRELVM v); /*serialization*/ SQUIRREL_API SQRESULT sq_writeclosure(HSQUIRRELVM vm,SQWRITEFUNC writef,SQUserPointer up); SQUIRREL_API SQRESULT sq_readclosure(HSQUIRRELVM vm,SQREADFUNC readf,SQUserPointer up); SQUIRREL_API SQRESULT sq_limitthreadaccess(HSQUIRRELVM vm, int64_t tid); SQUIRREL_API bool sq_canaccessfromthisthread(HSQUIRRELVM vm); typedef struct SQAllocContextT * SQAllocContext; SQUIRREL_API SQAllocContext sq_getallocctx(HSQUIRRELVM v); /*mem allocation*/ SQUIRREL_API void *sq_malloc(SQAllocContext ctx, SQUnsignedInteger size); SQUIRREL_API void *sq_realloc(SQAllocContext ctx, void* p,SQUnsignedInteger oldsize,SQUnsignedInteger newsize); SQUIRREL_API void sq_free(SQAllocContext ctx, void *p,SQUnsignedInteger size); /*debug*/ SQUIRREL_API SQRESULT sq_stackinfos(HSQUIRRELVM v,SQInteger level,SQStackInfos *si); SQUIRREL_API void sq_setdebughook(HSQUIRRELVM v); SQUIRREL_API void sq_setnativedebughook(HSQUIRRELVM v,SQDEBUGHOOK hook); SQUIRREL_API SQGETTHREAD sq_set_thread_id_function(HSQUIRRELVM v, SQGETTHREAD func); SQUIRREL_API SQSQCALLHOOK sq_set_sq_call_hook(HSQUIRRELVM v, SQSQCALLHOOK hook); SQUIRREL_API SQCOMPILELINEHOOK sq_set_compile_line_hook(HSQUIRRELVM v, SQCOMPILELINEHOOK hook); SQUIRREL_API void sq_forbidglobalconstrewrite(HSQUIRRELVM v, SQBool on); /*watchdog*/ SQUIRREL_API SQWATCHDOGHOOK sq_set_watchdog_hook(HSQUIRRELVM v, SQWATCHDOGHOOK hook); SQUIRREL_API void sq_kick_watchdog(HSQUIRRELVM v); SQUIRREL_API SQInteger sq_set_watchdog_timeout_msec(HSQUIRRELVM v, SQInteger timeout); /*static analysis*/ SQUIRREL_API void sq_resetanalyzerconfig(); SQUIRREL_API bool sq_loadanalyzerconfig(const char *configFileName); SQUIRREL_API bool sq_loadanalyzerconfigblk(const KeyValueFile &config); SQUIRREL_API bool sq_setdiagnosticstatebyname(const char *diagId, bool val); SQUIRREL_API bool sq_setdiagnosticstatebyid(int32_t id, bool val); SQUIRREL_API void sq_printwarningslist(FILE *ostream); SQUIRREL_API void sq_enablesyntaxwarnings(bool on); SQUIRREL_API void sq_checkglobalnames(HSQUIRRELVM v); SQUIRREL_API void sq_mergeglobalnames(const HSQOBJECT *bindings); /*UTILITY MACRO*/ #define sq_isnumeric(o) ((o)._type&SQOBJECT_NUMERIC) #define sq_istable(o) ((o)._type==OT_TABLE) #define sq_isarray(o) ((o)._type==OT_ARRAY) #define sq_isfunction(o) ((o)._type==OT_FUNCPROTO) #define sq_isclosure(o) ((o)._type==OT_CLOSURE) #define sq_isgenerator(o) ((o)._type==OT_GENERATOR) #define sq_isnativeclosure(o) ((o)._type==OT_NATIVECLOSURE) #define sq_isstring(o) ((o)._type==OT_STRING) #define sq_isinteger(o) ((o)._type==OT_INTEGER) #define sq_isfloat(o) ((o)._type==OT_FLOAT) #define sq_isuserpointer(o) ((o)._type==OT_USERPOINTER) #define sq_isuserdata(o) ((o)._type==OT_USERDATA) #define sq_isthread(o) ((o)._type==OT_THREAD) #define sq_isnull(o) ((o)._type==OT_NULL) #define sq_isclass(o) ((o)._type==OT_CLASS) #define sq_isinstance(o) ((o)._type==OT_INSTANCE) #define sq_isbool(o) ((o)._type==OT_BOOL) #define sq_isweakref(o) ((o)._type==OT_WEAKREF) #define sq_type(o) ((o)._type) #define sq_objflags(o) ((o)._flags) #define SQ_OK (0) #define SQ_ERROR (-1) #define SQ_FAILED(res) ((res)<0) #define SQ_SUCCEEDED(res) ((res)>=0) #if defined(__GNUC__) || defined(__clang__) # define SQ_UNUSED_ARG(x) x __attribute__((__unused__)) #else # define SQ_UNUSED_ARG(x) #endif #ifdef __cplusplus } /*extern "C"*/ #endif static inline void sq_addref(HSQUIRRELVM v,HSQOBJECT *po) { if (ISREFCOUNTED(sq_type(*po))) sq_addref_refcounted(v, po); } static inline SQBool sq_release(HSQUIRRELVM v,HSQOBJECT *po) { return !ISREFCOUNTED(sq_type(*po)) || sq_release_refcounted(v, po); } /* Removed SQObjectPtr overload to forbid dangerous cast to SQObjectPtr. Passing SQObjectPtr instead of SQObject to some quirrel API functions can cause unwanted call of extra Release() which may lead to memory corruption. This function is explicitly deleted to prevent such errors. */ struct SQObjectPtr; SQUIRREL_API SQRESULT sq_getstackobj(HSQUIRRELVM v,SQInteger idx, SQObjectPtr *po) = delete; SQUIRREL_API SQRESULT sq_obj_get(HSQUIRRELVM v, const HSQOBJECT *obj, const HSQOBJECT *slot, SQObjectPtr *out, bool raw) = delete; #endif /*_SQUIRREL_H_*/ ================================================ FILE: internal/sq_safe_shift.h ================================================ #pragma once #ifndef SQ_SAFE_SHIFT_H #define SQ_SAFE_SHIFT_H #include #include #include static constexpr unsigned SQ_INTEGER_BITS = std::numeric_limits::digits; static constexpr unsigned SQ_INTEGER_MASK = SQ_INTEGER_BITS - 1; inline SQInteger sq_safe_shift_left(SQInteger x, SQInteger y) { SQUnsignedInteger ux = (SQUnsignedInteger)x; SQUnsignedInteger uy = (SQUnsignedInteger)y; SQUnsignedInteger neg = uy >> (SQ_INTEGER_BITS - 1); SQUnsignedInteger neg_mask = ~neg + 1u; // == -neg without C4146 SQUnsignedInteger sh = (uy ^ neg_mask) + neg; SQUnsignedInteger overflow = ~SQUnsignedInteger(sh >= SQ_INTEGER_BITS) + 1u; SQUnsignedInteger l = ux << (sh & SQ_INTEGER_MASK); SQUnsignedInteger r = ux >> (sh & SQ_INTEGER_MASK); // Arithmetic sign extension for reversed right shift (negative y) SQUnsignedInteger sign = ~SQUnsignedInteger(x < 0) + 1u; SQUnsignedInteger sign_ext = sign ^ (sign >> (sh & SQ_INTEGER_MASK)); r |= sign_ext; SQUnsignedInteger res = (l & ~neg_mask) | (r & neg_mask); // On overflow: 0 for left shift (positive y), sign-fill for reversed right shift (negative y) SQUnsignedInteger fill = sign & neg_mask & overflow; res = (res & ~overflow) | fill; return (SQInteger)res; } inline SQInteger sq_safe_shift_right(SQInteger x, SQInteger y) { SQUnsignedInteger ux = (SQUnsignedInteger)x; SQUnsignedInteger uy = (SQUnsignedInteger)y; SQUnsignedInteger neg = uy >> (SQ_INTEGER_BITS - 1); SQUnsignedInteger neg_mask = ~neg + 1u; SQUnsignedInteger sh = (uy ^ neg_mask) + neg; SQUnsignedInteger overflow = ~SQUnsignedInteger(sh >= SQ_INTEGER_BITS) + 1u; SQUnsignedInteger r = ux >> (sh & SQ_INTEGER_MASK); SQUnsignedInteger l = ux << (sh & SQ_INTEGER_MASK); // Arithmetic sign extension: fill top sh bits of r with sign bit SQUnsignedInteger sign = ~SQUnsignedInteger(x < 0) + 1u; SQUnsignedInteger sign_ext = sign ^ (sign >> (sh & SQ_INTEGER_MASK)); r |= sign_ext; SQUnsignedInteger dir = (r & ~neg_mask) | (l & neg_mask); SQUnsignedInteger fill = sign & overflow; return (SQInteger)((dir & ~overflow) | fill); } inline SQInteger sq_safe_unsigned_shift_right(SQInteger x, SQInteger y) { SQUnsignedInteger ux = (SQUnsignedInteger)x; SQUnsignedInteger uy = (SQUnsignedInteger)y; SQUnsignedInteger neg = uy >> (SQ_INTEGER_BITS - 1); SQUnsignedInteger neg_mask = ~neg + 1u; SQUnsignedInteger sh = (uy ^ neg_mask) + neg; SQUnsignedInteger overflow = ~SQUnsignedInteger(sh >= SQ_INTEGER_BITS) + 1u; SQUnsignedInteger r = ux >> (sh & SQ_INTEGER_MASK); SQUnsignedInteger l = ux << (sh & SQ_INTEGER_MASK); SQUnsignedInteger res = (r & ~neg_mask) | (l & neg_mask); res &= ~overflow; return (SQInteger)res; } #endif // SQ_SAFE_SHIFT_H ================================================ FILE: internal/sqstringlib.h ================================================ #pragma once #include SQInteger _sq_string_strip_impl(HSQUIRRELVM v, SQInteger arg_stack_start); SQInteger _sq_string_lstrip_impl(HSQUIRRELVM v, SQInteger arg_stack_start); SQInteger _sq_string_rstrip_impl(HSQUIRRELVM v, SQInteger arg_stack_start); SQInteger _sq_string_split_by_chars_impl(HSQUIRRELVM v, SQInteger arg_stack_start); SQInteger _sq_string_escape_impl(HSQUIRRELVM v, SQInteger arg_stack_start); SQInteger _sq_string_startswith_impl(HSQUIRRELVM v, SQInteger arg_stack_start); SQInteger _sq_string_endswith_impl(HSQUIRRELVM v, SQInteger arg_stack_start); ================================================ FILE: scripts/samples/ackermann.nut ================================================ /* * * Original Javascript version by David Hedbor(http://www.bagley.org/~doug/shootout/) * */ let {max} = require("math") function Ack(M, N) { if (M == 0) return N + 1 if (N == 0) return Ack(M-1, 1) return Ack(M - 1, Ack(M, (N-1))) } local n if(vargv.len()!=0) { n = max(1, vargv[0].tointeger()) } else { n = 1 } println($"n={n}") println($"Ack(3,{n}):{Ack(3, n)}") ================================================ FILE: scripts/samples/array.nut ================================================ /* * * Original Javascript version by David Hedbor(http://www.bagley.org/~doug/shootout/) * */ local n, i, k if (vargv.len()!=0) { n = ::max(1, vargv[0].tointeger()) } else { n = 1 } let x = array(n) let y = array(n) for (i = 0; i < n; i+=1) { x[i] = i + 1 y[i] = 0 } for (k = 0 ; k < n; k+=1) { for (i = n-1; i >= 0; i-=1) { y[i] = y[i]+ x[i] } } println($"{y[0]} {y[n-1]}") ================================================ FILE: scripts/samples/class.nut ================================================ class BaseVector { constructor(...) { if (vargv.len() >= 3) { this.x = vargv[0] this.y = vargv[1] this.z = vargv[2] } } x = 0 y = 0 z = 0 } class Vector3(BaseVector) { function _add(other) { local cls = this.getclass() if (other instanceof cls) return cls(this.x+other.x, this.y+other.y, this.z+other.z) else throw "wrong parameter" } function Print() { println($"{this.x}, {this.y}, {this.z}") } } let v0 = Vector3(1,2,3) let v1 = Vector3(11,12,13) let v2 = v0 + v1 v2.Print() ================================================ FILE: scripts/samples/fibonacci.nut ================================================ /* * * Original Javascript version by David Hedbor(http://www.bagley.org/~doug/shootout/) * */ function fib(n) { if (n < 2) return 1 return fib(n-2) + fib(n-1) } local n = vargv.len()!=0 ? vargv[0].tointeger() : 1 println(fib(n)) ================================================ FILE: scripts/samples/flow.nut ================================================ #allow-switch-statement let {min, max} = require("math") if (min(100,200) > max(50,20)) println("I'm useless statement just to show up the if/else") else println("squirrel!!") print("\n") function typy(obj) { switch(typeof obj) { case "integer": case "float": return "is a number" case "table": case "array": return "is a container" default: return "is other stuff" } } let a=1, b={} function c(a,b){return a+b} println($"a {typy(a)}") println($"b {typy(b)}") println($"c {typy(c)}") ================================================ FILE: scripts/samples/generators.nut ================================================ /* *Random number function from The Great Computer Language shootout *converted to a generator func */ function gen_random(max) { local last=42 local IM = 139968 local IA = 3877 local IC = 29573 for (;;) { //loops forever last = (last * IA + IC) % IM yield (max * last / IM) } } let randtor = gen_random(100) println("RAND NUMBERS") for (local i=0;i<10;i+=1) println($"> {resume randtor}") println("FIBONACCI") function fiboz(n) { local prev=0 local curr=1 yield 1 for (local i=0;i {val}") } ================================================ FILE: scripts/samples/hello.nut ================================================ println("Hello World!") ================================================ FILE: scripts/samples/list.nut ================================================ /*translation of the list test from The Great Computer Language Shootout */ function compare_arr(a1,a2) { foreach(i,val in a1) if(val!=a2[i]) return null return 1 } function test() { let size=10000 let l1 = array(size) for (local i=0; i0) l3.append(l2.pop()) while(l3.len()>0) l2.append(l3.pop()) l1.reverse() if (compare_arr(l1,l2)) return l1.len() return null } let n = vargv.len()!=0?vargv[0].tointeger():1 for(local i=0;i") else println("") ================================================ FILE: scripts/samples/methcall.nut ================================================ /*translation of the methcall test from The Great Computer Language Shootout */ let datetime = require("datetime") class Toggle { bool=null constructor(startstate) { this.bool = startstate } function value() { return this.bool } function activate() { this.bool = !this.bool return this } } class NthToggle(Toggle) { count_max=null count=0 constructor(start_state, max_counter) { base.constructor(start_state) this.count_max = max_counter } function activate() { ++this.count if (this.count >= this.count_max) { base.activate() this.count = 0 } return this } } local function main() { let n = vargv.len()!=0?vargv[0].tointeger():1 local val = 1 let toggle = Toggle(val) local i = n while(i--) { val = toggle.activate().value() } println(toggle.value() ? "true" : "false") val = 1 local ntoggle = NthToggle(val, 3) i = n while(i--) { val = ntoggle.activate().value() } println(ntoggle.value() ? "true" : "false") } let start=datetime.clock() main() println("TIME="+(datetime.clock()-start)) ================================================ FILE: scripts/samples/module_1.nut ================================================ function foo() { return "" } return foo ================================================ FILE: scripts/samples/module_2.nut ================================================ let exports = { bar = "BAR" baz = "BAZ" } return exports ================================================ FILE: scripts/samples/module_demo.nut ================================================ let foo = require("module_1.nut") let {bar, baz} = require("module_2.nut") println($"foo() result = {foo()}") println($"bar = {bar}, baz = {baz}") ================================================ FILE: scripts/samples/regex.nut ================================================ let string = require("string") { let ex = string.regexp("[a-zA-Z]+") let s = "123 Test; strlen(str);" let res = ex.search(s) println(s.slice(res.begin,res.end)) //prints "Test" } { let ex = string.regexp(@"\m()"); let s = "123 Test; doSomething(str, getTemp(), (a+(b/c)));" let res = ex.search(s) println(s.slice(res.begin,res.end)) //prints "(...)" } ================================================ FILE: scripts/samples/tailstate.nut ================================================ local state1, state2, state3 state1 = function state1() { suspend("state1") return state2() } state2 = function state2() { suspend("state2") return state3() } state3 = function state3() { suspend("state3") return state1() } let statethread = newthread(state1) println(statethread.call()) for (local i = 0; i < 10000; i++) println(statethread.wakeup()) ================================================ FILE: scripts/std/analyzer.nut ================================================ function dynamic_content(o) { assert(type(o) == "table", $"expected table in dynamic_content(), got '{type(o)}'") o.__dynamic_content__ <- true return o } return { dynamic_content } ================================================ FILE: scripts/std/functools.nut ================================================ local abs = @(v) v> 0 ? v.tointeger() : -v.tointeger() local callableTypes = ["function","table","instance"] local function isCallable(v) { return callableTypes.indexof(::type(v)) != null && (v.getfuncinfos() != null) } /* + partial: partial(f(x,y,z), 1) == @(y,z) f(1,y,z) partial(f(x,y,z), 1, 2) == @(z) f(1,2,z) partial(f(x,y,z), 1, 2, 3) == @() f(1,2,3) or f(1,2,3) */ local function partial(func, ...){ ::assert(isCallable(func), "partial can be applied only to functions as first arguments") local infos = func.getfuncinfos() local argsnum = infos.parameters.len()-1 local isvargved = infos.varargs==1 local pargs = vargv local pargslen = pargs.len() if ( (pargslen == argsnum) && !isvargved) { return function(){ return func.acall([null].extend(pargs)) } } if ( (pargslen <= argsnum) || isvargved) { return function(...){ return func.acall([null].extend(pargs).extend(vargv)) } } ::assert(false, @() $"function '{infos.name}' cannot be partial with more arguments({pargslen}) that it accepts({argsnum})") return func } /* kwarg function: foo(x,y,z) kwarg(foo)==@(p) (foo(p?.x, p?.y, p?.z)) foo(x,y,z=2) kwarg(foo)==@(p) (foo(p?.x, p?.y, p?.z ?? 2)) */ local function kwarg(func){ ::assert(isCallable(func), "kwarg can be applied only to functions as first arguments") local infos = func.getfuncinfos() local funcargs = infos.parameters.slice(1) local defargs = infos.defparams local argsnum = funcargs.len() local isvargved = infos.varargs==1 local kfuncargs = {} local mandatoryparams = [] local defparamsStartFrom = argsnum-defargs.len() foreach (idx, arg in funcargs) { if (idx >= defparamsStartFrom) { kfuncargs[arg] <- defargs[idx-defparamsStartFrom] } else{ kfuncargs[arg] <-null mandatoryparams.append(arg) } } return !isvargved ? function(params=kfuncargs){ ::assert(["table", "class","instance"].indexof(::type(params))!=null, @() $"param of function can be only hashable (table, class, instance), found:'{::type(params)}'") local keys = params.keys() local nonManP = mandatoryparams.filter(@(p) keys.indexof(p) == null) ::assert(nonManP.len()==0, @() "not all mandatory parameters provided: {0}".subst(nonManP.len()==1 ? $"'{nonManP[0]}'" : nonManP.reduce(@(a,b) $"{a},'{b}'"))) params = kfuncargs.__merge(params) local posarguments = funcargs.map(@(kv) params[kv]) return func.acall([this].extend(posarguments)) } : function(params, ...){ ::assert(["table", "class","instance"].indexof(::type(params))!=null, @() $"param of function can be only hashable (table, class, instance), found:'{::type(params)}'") local keys = params.keys() local nonManP = mandatoryparams.filter(@(p) keys.indexof(p) == null) ::assert(nonManP.len()==0, @() "not all mandatory parameters provided: {0}".subst(nonManP.len()==1 ? $"'{nonManP[0]}'" : nonManP.reduce(@(a,b) $"{a},'{b}'"))) local posarguments = funcargs.map(@(kv) params[kv]) return func.acall([this].extend(posarguments).extend(vargv)) } } /* kwpartial local function foo(a,b,c){(a+b)*c} partial(foo, {b=3})(1,5) == (1+3)*5 partial(foo, {b=3}, 2)(5) == (2+3)*5 */ local function kwpartial(func, partparams, ...){ ::assert(isCallable(func), "partial can be applied only to functions as first arguments") ::assert(["table", "class","instance"].indexof(::type(partparams))!=null, "kwpartial second argument of function can be only hashable (table, class, instance)") local infos = func.getfuncinfos() local funcargs = infos.parameters.slice(1) // local defargs = infos.defparams local argsnum = funcargs.len() local posfuncargs = {} local partvargs = vargv foreach (p, v in partparams){ local posidx = funcargs.indexof(p) if (posidx == null) continue posfuncargs[posidx] <- v } return function(...){ local curargs = partvargs.extend(vargv) ::assert(curargs.len()+posfuncargs.len()>=argsnum, @() $"not enough arguments provided for function '{infos?.name}' to call") local finalargs = [] local provArgIdx = 0 for (local i=0; i0, "pipe should be called with functions") local finfos = args[0].getfuncinfos() local numarg = (finfos.native ? abs(finfos.paramscheck) : finfos.parameters.len()) - 1 local isvargved = finfos.native ? finfos.paramscheck < -2 : finfos.varargs==1 ::assert(numarg == 1 && !isvargved, "pipe cannot be applied to vargv function call or multiarguments function call") return function(x){ foreach(v in args) x = v(x) return x } } // compose (reverse to pipe): // compose(f,g) = @(x) g(f(x)) local function compose(...){ local args = vargv.filter(isCallable).reverse() ::assert(args.len() == vargv.len() && args.len()>0, "compose should be called with functions") local finfos = args[0].getfuncinfos() local numarg = (finfos.native ? abs(finfos.paramscheck) : finfos.parameters.len()) - 1 local isvargved = finfos.native ? finfos.paramscheck < -2 : finfos.varargs==1 ::assert(numarg == 1 && !isvargved, "compose cannot be applied to vargv function call or multiarguments function call") return function(x){ foreach(v in args) x = v(x) return x } } /* (un)curry: cf = curry(f) == @(x) @(y) @(z) f(x,y,z) f(x,y,z) = cf(x)(y)(z) cf(x) == @(y) @(z) f(x,y,z) local get = curry(function(property, object){ return object?[property] }) local map = curry(function(fn, value){ return value.map(fn) }) local objects = [{ id = 1 }, { id = 2 }, { id = 3 }] local getIDs = map(get("id")) log(objects.map(get("id"))) //= [1, 2, 3] log(objects.map(@(v) v?.id)) //= [1, 2, 3] log(getIDs(objects)) //= [1, 2, 3] also our curry is (un)curry - so local sum = curry(@(a,b,c) a+b+c) sum(1)(2)(3) == sum(1)(2,3) == sum(1,2,3) == sum(1,2)(3) unfortunately returning function are now use vargv, instead of rest of parameters (the same issue goes to partial) */ local function curry(fn) { local finfos = fn.getfuncinfos() ::assert(!finfos.native || finfos.paramscheck >= 0, "Cannot curry native function with varargs") local arity = (finfos.native ? finfos.paramscheck : finfos.parameters.len())-1 return function f1(...) { local args = vargv if (args.len() >= arity) { return fn.acall([this].extend(args)) } else { local fone = f1 return function(...) { local moreArgs = vargv local newArgs = clone args newArgs.extend(moreArgs) return fone.acall([this].extend(newArgs)) } } } } /** * memoize(function, [hashFunction]) Memoizes a given function by caching the computed result. Useful for speeding up slow-running computations. If passed an optional hashFunction, it will be used to compute the hash key for storing the result, based on the arguments to the original function. The default hashFunction just uses the first argument to the memoized function as the key. */ local function memoize(func, hashfunc=null){ local cacheDefault = {} local cacheForNull = {} local parameters = func.getfuncinfos().parameters.slice(0) ::assert(parameters.len()>0) hashfunc = hashfunc ?? function(...) { return vargv[0] } local function memoizedfunc(...){ local args = [null].extend(vargv) local rawHash = hashfunc.acall(args) //index cannot be null. use different cache to avoid collision local hash = rawHash ?? 0 local cache = rawHash != null ? cacheDefault : cacheForNull if (hash in cache) { return cache[hash] } local result = func.acall(args) cache[hash] <- result return result } return memoizedfunc } //the same function as in underscore.js //Creates a version of the function that can only be called one time. //Repeated calls to the modified function will have no effect, returning the value from the original call. //Useful for initialization functions, instead of having to set a boolean flag and then check it later. local function once(func){ local result local called = false local function memoizedfunc(...){ if (called) return result local res = func.acall([null].extend(vargv)) result = res called = true return res } return memoizedfunc } //the same function as in underscore.js //Creates a version of the function that can be called no more than count times. //The result of the last function call is memoized and returned when count has been reached. local function before(count, func){ local called = 0 local res return function beforeTimes(...){ if (called >= count) return res called++ res = func.acall([null].extend(vargv)) return res } } //the same function as in underscore.js //Creates a version of the function that will only be run after being called count times. //Useful for grouping asynchronous responses, where you want to be sure that all the async calls have finished, before proceeding. local function after(count, func){ local called = 0 return function beforeTimes(...){ if (called < count) { called++ return } return func.acall([null].extend(vargv)) } } return { partial = partial pipe = pipe compose = compose kwarg = kwarg kwpartial = kwpartial curry = curry memoize = memoize isCallable = isCallable once = once before = before after = after } ================================================ FILE: sq/CMakeLists.txt ================================================ add_executable(sq sq.cpp sq_test_natives.cpp) target_link_libraries(sq squirrel sqstdlib sqmodules quirrel-compiler) target_include_directories(sq PRIVATE # include/ and sqmodules/ are transitive from linked libraries "$" "$" # Needed for sqtypeparser.h ) ================================================ FILE: sq/sq.cpp ================================================ /* see copyright notice in squirrel.h */ #include #include #include #include #include #include #include #if defined(_MSC_VER) && defined(_DEBUG) #include #include #endif #include #include #include #include #include #include #include #include #include #include #include #include #define scvprintf vfprintf static SqModules *module_mgr = nullptr; static DefSqModulesFileAccess file_access; static bool check_stack_mode = false; void PrintVersionInfos(); void printfunc(HSQUIRRELVM SQ_UNUSED_ARG(v),const char *s,...) { va_list vl; va_start(vl, s); scvprintf(stdout, s, vl); va_end(vl); } static FILE *errorStream = stderr; void errorfunc(HSQUIRRELVM SQ_UNUSED_ARG(v),const char *s,...) { va_list vl; va_start(vl, s); scvprintf(errorStream, s, vl); va_end(vl); } void PrintVersionInfos() { fprintf(stdout,"%s %s (%d bits)\n",SQUIRREL_VERSION,SQUIRREL_COPYRIGHT,((int)(sizeof(SQInteger)*8))); } void PrintUsage() { fprintf(stderr,"Usage: sq [options]\n" "Available options are:\n" " -c compiles the file to bytecode (default output 'out.cnut')\n" " -o specifies output file for the -c option\n" " -ast-dump [out-file] dump AST into console or file if specified\n" " -nodes-location print AST nodes locations\n" " -absolute-path use absolute path when print diangostics\n" " -bytecode-dump [out-file] dump SQ bytecode into console or file if specified\n" " -diag-file file write diagnostics into specified file\n" " -sa enable static analyzer\n" " --check-stack check stack after each script execution\n" " --warnings-list print all warnings and exit\n" " --parse-types parse function types from file\n" " --D: disable diagnostic by text id\n" " -optCH enable Closure Hoisting Optimization\n" " -d generates debug infos\n" " -v displays version\n" " -h prints help\n"); } static std::string read_file_ignoring_utf8bom(const char *filename) { FILE *f = fopen(filename, "rb"); if (!f) return std::string(); int length = 0; fseek(f, 0, SEEK_END); length = ftell(f); fseek(f, 0, SEEK_SET); std::string result; result.resize(length + 1); size_t sz = fread(&result[0], 1, length, f); fclose(f); result[sz] = 0; static const unsigned char bom[] = {0xEF, 0xBB, 0xBF}; if (sz >= 3 && memcmp(result.data(), bom, 3) == 0) result.erase(0, 3); return result; } static int fill_stack(HSQUIRRELVM v) { if (!check_stack_mode) return 0; for (int i = 0; i < 8; i++) sq_pushinteger(v, i + 1000000); return sq_gettop(v); } static bool check_stack(HSQUIRRELVM v, int expected_top) { if (!check_stack_mode) return true; int top = sq_gettop(v); if (top != expected_top) { fprintf(errorStream, "ERROR: Stack top is %d, expected %d\n", top, expected_top); return false; } for (int i = 0; i < 8; i++) { SQInteger val = -1; if (SQ_FAILED(sq_getinteger(v, -8 + i, &val)) || val != i + 1000000) { fprintf(errorStream, "ERROR: Stack value at %d is %d, expected %d\n", -8 + i, int(val), i + 1000000); return false; } } return true; } struct DumpOptions { bool astDump; bool bytecodeDump; bool nodesLocation; const char *astDumpFileName; const char *bytecodeDumpFileName; }; static void dumpAst_callback(HSQUIRRELVM vm, SQCompilation::SqASTData *astData, void *opts) { if (opts == NULL) return; DumpOptions *dumpOpt = (DumpOptions *)opts; if (dumpOpt->astDump) { if (dumpOpt->astDumpFileName) { FileOutputStream fos(dumpOpt->astDumpFileName); if (fos.valid()) { sq_dumpast(vm, astData, dumpOpt->nodesLocation, &fos); } else { printf("Error: cannot open AST dump file '%s'\n", dumpOpt->astDumpFileName); } } else { FileOutputStream fos(stdout); sq_dumpast(vm, astData, dumpOpt->nodesLocation, &fos); } } } static void dumpBytecodeAst_callback(HSQUIRRELVM vm, HSQOBJECT obj, void *opts) { if (opts == NULL) return; DumpOptions *dumpOpt = (DumpOptions *)opts; if (dumpOpt->bytecodeDump) { if (dumpOpt->bytecodeDumpFileName) { FileOutputStream fos(dumpOpt->bytecodeDumpFileName); if (fos.valid()) { sq_dumpbytecode(vm, obj, &fos); } else { printf("Error: cannot open Bytecode dump file '%s'\n", dumpOpt->bytecodeDumpFileName); } } else { FileOutputStream fos(stdout); sq_dumpbytecode(vm, obj, &fos); } } } static bool search_sqconfig(const char * initial_file_name, char *buffer, size_t bufferSize) { if (!initial_file_name) return false; size_t curSize = bufferSize; char *ptr = buffer; const char * slash1 = strrchr(initial_file_name, '\\'); const char * slash2 = strrchr(initial_file_name, '/'); const char * slash = slash1 > slash2 ? slash1 : slash2; if (slash) { size_t prefixSize = slash - initial_file_name + 1; if (prefixSize > curSize) return false; memcpy(buffer, initial_file_name, prefixSize); curSize -= prefixSize; ptr += prefixSize; } static const char configName[] = ".sqconfig"; const size_t configNameSize = (sizeof configName) - 1; static const char upDir[] = "../"; const size_t upDirSize = (sizeof upDir) - 1; char *dirPtr = ptr; memcpy(ptr, configName, configNameSize); ptr += configNameSize; curSize -= configNameSize; ptr[0] = '\0'; for (int i = 0; i < 16 && curSize > 0; i++) { if (FILE * f = fopen(buffer, "rt")) { fclose(f); return true; } if (curSize > upDirSize) { memcpy(dirPtr, upDir, upDirSize); dirPtr += upDirSize; curSize -= upDirSize; ptr += upDirSize; memcpy(dirPtr, configName, configNameSize); ptr[0] = '\0'; } else { break; } } return false; } static bool checkOption(char *argv[], int argc, const char *option, const char *&optArg) { optArg = nullptr; for (int i = 1; i< argc; ++i) { const char *arg = argv[i]; if (arg[0] == '-' && strcmp(arg + 1, option) == 0) { if ((i + 1) < argc) { const char *arg2 = argv[i + 1]; if (arg2[0] != '-') { // not next option optArg = arg2; } } return true; } } return false; } bool ends_with(const char * str, const char * suffix) { const char * s = strstr(str, suffix); return s && s[strlen(suffix)] == 0; } static bool parse_types_from_file(HSQUIRRELVM sqvm, const char *filename) { std::string code = read_file_ignoring_utf8bom(filename); if (code.empty()) return false; const char *c = code.c_str(); std::string line; int lineNum = 1; while (*c) { if (*c == '\n' || c[1] == 0) { if (c[1] == 0) line += *c; if (!line.empty()) { printf("%s\n", line.c_str()); SQFunctionType t(_ss(sqvm)); SQInteger errorPos; SQObjectPtr errorString; if (sq_parse_function_type_string(sqvm, line.c_str(), t, errorPos, errorString)) { SQObjectPtr s = sq_stringify_function_type(sqvm, t); printf("%s\n", _stringval(s)); printf(" functionName: %s\n", _stringval(t.functionName)); printf(" returnTypeMask: 0x%x\n", unsigned(t.returnTypeMask)); printf(" objectTypeMask: 0x%x\n", unsigned(t.objectTypeMask)); printf(" ellipsisArgTypeMask: 0x%x\n", unsigned(t.ellipsisArgTypeMask)); printf(" requiredArgs: %d\n", int(t.requiredArgs)); printf(" argCount: %d\n", int(t.argTypeMask.size())); printf(" pure: %s\n", t.pure ? "true" : "false"); printf(" nodiscard: %s\n", t.nodiscard ? "true" : "false"); printf("\n"); } else { printf("ERROR: %s\n", _stringval(errorString)); printf("at %s:%d:%d\n\n", filename, lineNum, int(errorPos)); } line.clear(); } lineNum++; } else { if (*c != '\r') line += *c; } c++; } return true; } #define _DONE 2 #define _ERROR 3 //<> this func is a mess int getargs(HSQUIRRELVM v,int argc, char* argv[],SQInteger *retval) { assert(module_mgr != nullptr && "Module manager has to be initialized"); const char *optArg = nullptr; DumpOptions dumpOpt = { 0 }; FILE *diagFile = nullptr; int compiles_only = 0; bool static_analysis = checkOption(argv, argc, "sa", optArg); // TODO: refact ugly loop below using this function bool parse_types = false; if (static_analysis) { sq_enablesyntaxwarnings(true); } std::vector listOfNutFiles; const char * output = NULL; *retval = 0; if(argc>1) { int index=1, exitloop=0; while(index < argc && !exitloop) { const char *arg = argv[index]; if (arg[0] == '-' && arg[1] == '-') arg++; if (strcmp("-ast-dump", arg) == 0) { dumpOpt.astDump = true; if ((index + 1) < argc && argv[index + 1][0] != '-' && !ends_with(argv[index + 1], ".nut")) dumpOpt.astDumpFileName = argv[++index]; } else if (strcmp("-nodes-location", arg) == 0) { dumpOpt.nodesLocation = true; } else if (strcmp("-absolute-path", arg) == 0) { file_access.useAbsolutePath = true; } else if (strcmp("-parse-types", arg) == 0) { parse_types = true; } else if (strcmp("-d", arg) == 0) { sq_lineinfo_in_expressions(v, 1); } else if (strcmp("-diag-file", arg) == 0) { if (((index + 1) < argc) && argv[index + 1][0] != '-') { const char *fileName = argv[++index]; diagFile = fopen(fileName, "wb"); if (diagFile == NULL) { printf("Cannot open diagnostic output file '%s'\n", fileName); return _ERROR; } } else { printf("-diag-file option requires file name to be specified\n"); return _ERROR; } } else if (strcmp("-bytecode-dump", arg) == 0) { dumpOpt.bytecodeDump = 1; if (((index + 1) < argc) && argv[index + 1][0] != '-' && !ends_with(argv[index + 1], ".nut")) { dumpOpt.bytecodeDumpFileName = argv[++index]; } } else if (strcmp("-c", arg) == 0) { compiles_only = 1; } else if (strcmp("-o", arg) == 0) { index++; output = arg; } else if (strcmp("-check-stack", arg) == 0) { check_stack_mode = true; } else if (strcmp("-optCH", arg) == 0) { sq_setcompilationoption(v, CompilationOptions::CO_CLOSURE_HOISTING_OPT, true); } else if (strcmp("-v", arg) == 0 || strcmp("--version", arg) == 0) { PrintVersionInfos(); return _DONE; } else if (strcmp("-h", arg) == 0 || strcmp("--help", arg) == 0) { PrintVersionInfos(); PrintUsage(); return _DONE; } else if (strcmp("-sa", arg) == 0) { static_analysis = true; } else if (static_analysis && strncmp("-D:", arg, 3) == 0) { if (!sq_setdiagnosticstatebyname(&arg[3], false)) printf("Unknown warning ID: '%s'\n", &arg[3]); } else if (strcmp(arg, "-warnings-list") == 0) { sq_printwarningslist(stdout); return _DONE; } else if (arg[0] == '-') { PrintVersionInfos(); printf("unknown argument '%s'\n", arg); PrintUsage(); *retval = -1; return _ERROR; } else { listOfNutFiles.emplace_back(std::string(arg)); } index++; } module_mgr->up_data = &dumpOpt; module_mgr->onAST_cb = &dumpAst_callback; module_mgr->onBytecode_cb = &dumpBytecodeAst_callback; module_mgr->compilationOptions.doStaticAnalysis = static_analysis; if (static_analysis) { sq_setcompilationoption(v, CompilationOptions::CO_CLOSURE_HOISTING_OPT, false); } if (diagFile) { errorStream = diagFile; } // src file if (listOfNutFiles.empty()) { printf("Error: expected source file name\n"); return _ERROR; } if (parse_types) { for (const std::string & fn : listOfNutFiles) { if (!parse_types_from_file(v, fn.c_str())) return _ERROR; } return _DONE; } for (const std::string & fn : listOfNutFiles) { const char *filename=fn.c_str(); if (static_analysis) { sq_resetanalyzerconfig(); char buffer[1024]; if (search_sqconfig(filename, buffer, sizeof buffer)) { if (!sq_loadanalyzerconfig(buffer)) { fprintf(errorStream, "Cannot load .sqconfig file %s\n", buffer); return _ERROR; } } } if (compiles_only) { if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,SQTrue))){ const char *outfile = "out.cnut"; if(output) { outfile = output; } if(SQ_SUCCEEDED(sqstd_writeclosuretofile(v,outfile))) return _DONE; } } else { int top = fill_stack(v); Sqrat::Object exports; SqModules::string errMsg; int retCode = _DONE; if (!module_mgr->requireModule(filename, true, static_analysis ? SqModules::__analysis__ : SqModules::__main__, exports, errMsg)) { retCode = _ERROR; } if (retCode == _DONE && sq_isinteger(exports.GetObject())) { *retval = exports.GetObject()._unVal.nInteger; } if (static_analysis) { sq_checkglobalnames(v); } if (retCode != _DONE) { fprintf(stderr, "Error [%s]\n", errMsg.c_str()); } if (!check_stack(v, top)) { fprintf(stderr, "Stack check failed after execution of '%s'\n", filename); *retval = -3; retCode = _ERROR; } return retCode; } //if this point is reached an error occurred { const char *err; sq_getlasterror(v); if(SQ_SUCCEEDED(sq_getstring(v,-1,&err))) { printf("Error [%s]\n",err); *retval = -2; return _ERROR; } } } } else { PrintUsage(); *retval = -1; return _ERROR; } return _DONE; } int sq_interpreter_main(int argc, char* argv[]) { SQInteger retval = 0; HSQUIRRELVM v = sq_open(1024); sq_setprintfunc(v,printfunc,errorfunc); sqstd_seterrorhandlers(v); module_mgr = new SqModules(v, &file_access); module_mgr->registerMathLib(); module_mgr->registerStringLib(); module_mgr->registerSystemLib(); module_mgr->registerIoStreamLib(); module_mgr->registerIoLib(); module_mgr->registerDateTimeLib(); module_mgr->registerDebugLib(); sqstd_register_command_line_args(v, argc, argv); extern void register_test_natives(SqModules *); register_test_natives(module_mgr); //gets arguments getargs(v, argc, argv, &retval); if (errorStream != stderr) { fclose(errorStream); } delete module_mgr; sq_close(v); return int(retval); } #ifndef SQ_EXCLUDE_DEFAULT_MAIN int main(int argc, char* argv[]) { return sq_interpreter_main(argc, argv); } #endif ================================================ FILE: sq/sq_test_natives.cpp ================================================ // Test native module for native field testing. // Registers "test.native" module with NativeVec class. #include #include #include #include namespace { struct TestNativeVec { float x, y, z; int32_t w; }; static SQInteger nativevec_ctor(HSQUIRRELVM vm) { TestNativeVec *self = Sqrat::ClassType::GetInstance(vm, 1); if (!self) return SQ_ERROR; memset(self, 0, sizeof(TestNativeVec)); SQInteger top = sq_gettop(vm); if (top >= 2) { SQFloat f; sq_getfloat(vm, 2, &f); self->x = (float)f; } if (top >= 3) { SQFloat f; sq_getfloat(vm, 3, &f); self->y = (float)f; } if (top >= 4) { SQFloat f; sq_getfloat(vm, 4, &f); self->z = (float)f; } if (top >= 5) { SQInteger i; sq_getinteger(vm, 5, &i); self->w = (int32_t)i; } return 0; } } // namespace template<> struct Sqrat::InstanceToString { static SQInteger Format(HSQUIRRELVM vm) { TestNativeVec *self = Sqrat::ClassType::GetInstance(vm, 1); if (!self) return SQ_ERROR; char buf[128]; snprintf(buf, sizeof(buf), "NativeVec(%g, %g, %g, %d)", self->x, self->y, self->z, self->w); sq_pushstring(vm, buf, -1); return 1; } }; void register_test_natives(SqModules *module_mgr) { HSQUIRRELVM vm = module_mgr->getVM(); Sqrat::Class cls(vm, "NativeVec"); cls .UseInlineUserdata() .SquirrelCtor(nativevec_ctor, 0, ".nnnn") .NativeVar("x", &TestNativeVec::x) .NativeVar("y", &TestNativeVec::y) .NativeVar("z", &TestNativeVec::z) .NativeVar("w", &TestNativeVec::w); Sqrat::Table exports(vm); exports.Bind("NativeVec", cls); module_mgr->addNativeModule("test.native", exports); } ================================================ FILE: sqmodules/CMakeLists.txt ================================================ set(SQMODULES_SRC sqmodules.cpp helpers.cpp path.cpp deffileaccess.cpp ) add_library(sqmodules STATIC ${SQMODULES_SRC}) add_library(squirrel::sqmodules ALIAS sqmodules) target_link_libraries(sqmodules sqstdlib squirrel) target_include_directories(sqmodules PUBLIC "$" "$" "$" PRIVATE "$" ) ================================================ FILE: sqmodules/deffileaccess.cpp ================================================ #include "sqmodules.h" #include "path.h" #include "helpers.h" #ifdef _WIN32 # include #else # include # include # define _access access #endif #ifdef _WIN32 #define MAX_PATH_LENGTH _MAX_PATH static const char *computeAbsolutePath(const char *resolved_fn, char *buffer, size_t size) { return _fullpath(buffer, resolved_fn, size); } #else // _WIN32 #define MAX_PATH_LENGTH PATH_MAX static const char *computeAbsolutePath(const char *resolved_fn, char *buffer, size_t size) { (void)size; return realpath(resolved_fn, buffer); } #endif // _WIN32 void DefSqModulesFileAccess::destroy() { if (needToDeleteSelf) delete this; } SqModules::string DefSqModulesFileAccess::makeRelativeFilename(const char *cur_file, const char *requested_fn) { scriptPathBuf.resize(strlen(cur_file) + 2); dd_get_fname_location(&scriptPathBuf[0], cur_file); dd_append_slash_c(&scriptPathBuf[0]); size_t locationLen = strlen(&scriptPathBuf[0]); size_t reqFnLen = strlen(requested_fn); scriptPathBuf.resize(locationLen + reqFnLen + 1); strcpy(&scriptPathBuf[locationLen], requested_fn); dd_simplify_fname_c(&scriptPathBuf[0]); scriptPathBuf.resize(strlen(&scriptPathBuf[0]) + 1); return SqModules::string(scriptPathBuf.data()); } void DefSqModulesFileAccess::resolveFileName(const char *requested_fn, const char *running_script, string &res) { res.clear(); // try relative path first if (running_script) { string scriptPath = makeRelativeFilename(running_script, requested_fn); bool exists = _access(scriptPath.c_str(), 0) == 0; if (exists) res = std::move(scriptPath); } if (res.empty()) { scriptPathBuf.resize(strlen(requested_fn) + 1); strcpy(&scriptPathBuf[0], requested_fn); dd_simplify_fname_c(&scriptPathBuf[0]); res.insert(res.end(), scriptPathBuf.begin(), scriptPathBuf.begin() + strlen(&scriptPathBuf[0])); scriptPathBuf.clear(); } if (useAbsolutePath) { char buffer[MAX_PATH_LENGTH] = {0}; const char *real_path = computeAbsolutePath(res.c_str(), buffer, sizeof buffer); if (real_path) res = real_path; } } bool DefSqModulesFileAccess::readFile(const string &resolved_fn, const char *requested_fn, vector &buf, string &out_err_msg) { FILE* f = fopen(resolved_fn.c_str(), "rb"); if (!f) { out_err_msg = sqm::format_string("Script file %s (%s) not found", requested_fn, resolved_fn.c_str()); return false; } #ifdef _WIN32 long len = _filelength(_fileno(f)); #else fseek(f, 0, SEEK_END); long len = ftell(f); if (len < 0) { out_err_msg = sqm::format_string("Failed to read script file %s", resolved_fn.c_str()); fclose(f); return false; } fseek(f, 0, SEEK_SET); #endif buf.resize(len + 1); if (fread((void *)buf.data(), 1, len, f) != len) { out_err_msg = sqm::format_string("Failed to read script file %s (%s)", requested_fn, resolved_fn.c_str()); fclose(f); return false; } buf[len] = 0; fclose(f); return true; } ================================================ FILE: sqmodules/helpers.cpp ================================================ #include "helpers.h" #include namespace sqm { SqModules::string format_string(const char *fmt, ...) { va_list ap; va_start(ap, fmt); va_list ap_copy; va_copy(ap_copy, ap); int len = vsnprintf(nullptr, 0, fmt, ap_copy); va_end(ap_copy); if (len < 0) { va_end(ap); return {}; } SqModules::string s; s.resize(len + 1); vsnprintf(&s[0], s.size(), fmt, ap); va_end(ap); s.resize(len); return s; // no copy due to RVO or move } void append_format(SqModules::string &s, const char *fmt, ...) { va_list ap; va_start(ap, fmt); va_list ap_copy; va_copy(ap_copy, ap); const int len = vsnprintf(nullptr, 0, fmt, ap_copy); va_end(ap_copy); if (len < 0) { va_end(ap); return; } const size_t old = s.size(); s.resize(old + len + 1); vsnprintf(&s[old], len + 1, fmt, ap); va_end(ap); s.resize(old + len); } } ================================================ FILE: sqmodules/helpers.h ================================================ #pragma once #include "sqmodules.h" namespace sqm { SqModules::string format_string(const char *fmt, ...); void append_format(SqModules::string &s, const char *fmt, ...); } ================================================ FILE: sqmodules/path.cpp ================================================ #include "path.h" #include #include #ifndef _WIN32 #include #define _stricmp strcasecmp #endif // file path operations #define PATH_DELIM '/' #define PATH_DELIM_BACK '\\' #define PATH_DELIM_STR "/" #define MAX_PATH_LEN 260 void dd_simplify_fname_c(char *s) { if(!s) return; int i,len=strlen(s); // check for URL format to prevent removal of :// if (char *semi = (char*)memchr(s, ':', len > 8 ? 8 : len)) if (len > (semi+3-s) && semi[1] == '/' && semi[2] == '/') { len -= int(semi+3-s); s = semi+3; } for(;;) { bool ok=false; // remove leading spaces for(i=0;i0) {len-=i;memmove(s,s+i,len+1); ok=true;} // remove trailing spaces for(i=len-1;i>=0;--i) if(s[i]!=' ' && s[i]!='\t' && s[i]!='\n' && s[i]!='\r') break; if(i1) if (s[0]=='.' && s[1]==PATH_DELIM) { len-=2; memmove(s,s+2,len+1); } else break; for(i=1;i=0;--j) if(s[j]==PATH_DELIM || s[j]==':') break; if (++j 0 ) if ( fn[l-1] != PATH_DELIM_BACK && fn[l-1] != PATH_DELIM ) { fn[l]=PATH_DELIM; fn[l+1]=0; } } char *dd_get_fname_location(char *buf, const char *filename) { strcpy ( buf, filename ); dd_simplify_fname_c(buf); char* p = strrchr(buf, PATH_DELIM); if ( !p ) p = strchr(buf, ':'); if ( p ) p ++; else p = buf; *p = '\0'; return buf; } ================================================ FILE: sqmodules/path.h ================================================ #pragma once void dd_simplify_fname_c(char *s); bool dd_fname_equal(const char *fn1, const char *fn2); void dd_append_slash_c(char *fn); char *dd_get_fname_location(char *buf, const char *filename); ================================================ FILE: sqmodules/span.h ================================================ #pragma once #include #include #include #include #include namespace sqm { template class span { public: typedef T element_type; typedef typename SQRAT_STD::remove_cv::type value_type; typedef size_t size_type; typedef ptrdiff_t index_type; typedef T* pointer; typedef T& reference; typedef const T* const_pointer; typedef const T& const_reference; typedef T* iterator; typedef const T* const_iterator; span() noexcept : ptr_(nullptr), size_(0) {} span(pointer ptr, size_type count) : ptr_(ptr), size_(count) {} span(pointer first, pointer last) : ptr_(first), size_(static_cast(last - first)) { assert(last >= first); } template span(T (&arr)[N]) noexcept : ptr_(arr), size_(N) {} template ::value && SQRAT_STD::is_same::type>::value>::type> span(SQRAT_STD::vector& v) noexcept : ptr_(v.data()), size_(v.size()) {} template ::type>::value>::type> span(const SQRAT_STD::vector& v) noexcept : ptr_(v.data()), size_(v.size()) {} template ::value>::type> span(const span& other) noexcept : ptr_(other.data()), size_(other.size()) {} size_type size() const noexcept { return size_; } size_type size_bytes() const noexcept { return size_ * sizeof(T); } bool empty() const noexcept { return size_ == 0; } pointer data() const noexcept { return ptr_; } reference operator[](size_type i) const { return ptr_[i]; } reference at(size_type i) const { return ptr_[i]; } reference front() const { return *ptr_; } reference back() const { return ptr_[size_ - 1]; } iterator begin() const noexcept { return ptr_; } iterator end() const noexcept { return ptr_ + size_; } const_iterator cbegin() const noexcept { return ptr_; } const_iterator cend() const noexcept { return ptr_ + size_; } private: pointer ptr_; size_type size_; }; template inline span make_span(T* ptr, size_t count) { return span(ptr, count); } template inline span make_span(T* first, T* last) { return span(first, last); } template inline span make_span(T (&arr)[N]) { return span(arr); } template inline span make_span(SQRAT_STD::vector& v) { return span(v); } template inline span make_span(const SQRAT_STD::vector& v) { return span(v); } } ================================================ FILE: sqmodules/sqmodules.cpp ================================================ #include "sqmodules.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "helpers.h" #ifndef G_UNUSED #define G_UNUSED(x) ((void)(x)) #endif const char *SqModules::__main__ = "__main__"; const char *SqModules::__fn__ = nullptr; const char *SqModules::__analysis__ = "__analysis__"; using namespace sqm; static sqm::span skip_bom(sqm::span buf) { static constexpr uint8_t utf8_bom[] = {0xef, 0xbb, 0xbf}; bool isBom = (buf.size() >= sizeof(utf8_bom)) && memcmp(buf.data(), utf8_bom, sizeof(utf8_bom))==0; int utf8MarkOffset = isBom ? sizeof(utf8_bom) : 0; return sqm::make_span(buf.data() + utf8MarkOffset, buf.size() - utf8MarkOffset); } static SQInteger persist_state(HSQUIRRELVM vm) { HSQOBJECT hModuleThis, hSlot, hCtor, hMstate; sq_getstackobj(vm, 1, &hModuleThis); sq_getstackobj(vm, 2, &hSlot); sq_getstackobj(vm, 3, &hCtor); sq_getstackobj(vm, 4, &hMstate); Sqrat::Table mstate(hMstate, vm); SQRAT_ASSERTF(mstate.GetType() == OT_TABLE, "type = %X", mstate.GetType()); Sqrat::Object slot(hSlot, vm); if (mstate.RawHasKey(slot)) { Sqrat::Object curVal = mstate.GetSlot(slot); sq_pushobject(vm, curVal); return 1; } Sqrat::Object ctor(hCtor, vm); Sqrat::Function f(vm, Sqrat::Object(hModuleThis, vm), ctor); auto callRes = f.Eval(); if (!callRes) return sq_throwerror(vm, "Failed to call initializer"); Sqrat::Object &res = callRes.value(); SQObjectType tp = res.GetType(); bool isMutable = tp == OT_TABLE || tp == OT_ARRAY || tp == OT_USERDATA || tp == OT_CLASS || tp == OT_INSTANCE; if (!isMutable) { if (sq_isstring(hSlot)) return sqstd_throwerrorf(vm, "Persist '%s' is not mutable, probably an error", sq_objtostring(&hSlot)); else return sq_throwerror(vm, "Persist is not mutable, probably an error"); } mstate.SetValue(slot, res); sq_pushobject(vm, res); return 1; } static SQInteger keepref(HSQUIRRELVM vm) { // arguments: this, object, ref holder SQRAT_ASSERT(sq_gettype(vm, 3) == OT_ARRAY); sq_push(vm, 2); // push object SQRAT_VERIFY(SQ_SUCCEEDED(sq_arrayappend(vm, 3))); // append to ref holder sq_push(vm, 2); // push object again return 1; } SqModules::SqModules(HSQUIRRELVM vm, ISqModulesFileAccess *file_access) : sqvm(vm), fileAccess(file_access), beforeImportModuleCb(nullptr), up_data(nullptr), onAST_cb(nullptr), onBytecode_cb(nullptr) { compilationOptions.raiseError = true; compilationOptions.doStaticAnalysis = false; sq_enablesyntaxwarnings(false); registerTypesLib(); registerModulesLib(); } SqModules::~SqModules() { modules.swap(prevModules); modules.clear(); callAndClearUnloadHandlers(true); prevModules.clear(); fileAccess->destroy(); } bool SqModules::checkCircularReferences(const string &resolved_fn, const char *requested_fn, string &out_err_msg) { for (const string &scriptFn : runningScripts) { if (scriptFn == resolved_fn) { out_err_msg = format_string("Required script %s (%s) is currently being executed, possible circular require() call.\n" "require() stack:", requested_fn, resolved_fn.c_str()); for (const string &stackItem : runningScripts) append_format(out_err_msg, "\n * %s", stackItem.c_str()); return false; } } return true; } bool SqModules::compileScriptImpl(const sqm::span buf, const char *resolved_fn, Sqrat::Table &bindings, string &out_err_msg, SQCompilation::SqASTData **return_ast) { out_err_msg.clear(); if (onCompileFile_cb) onCompileFile_cb(sqvm, resolved_fn); SQCompilation::SqASTData *ast = sq_parsetoast(sqvm, buf.data(), buf.size(), resolved_fn, compilationOptions.doStaticAnalysis, compilationOptions.raiseError); if (return_ast) *return_ast = ast; if (!ast) return false; if (onAST_cb) onAST_cb(sqvm, ast, up_data); SQModuleImport *imports = nullptr; SQInteger numImports = 0; bool importsRes = SQ_SUCCEEDED(sq_getimports(sqvm, ast, &numImports, &imports)); SQRAT_ASSERT(importsRes); G_UNUSED(importsRes); bool importRes = importModules(resolved_fn, bindings, imports, numImports, out_err_msg); sq_freeimports(sqvm, numImports, imports); if (!importRes) { sq_releaseASTData(sqvm, ast); if (return_ast) *return_ast = nullptr; return false; } HSQOBJECT hBindings = bindings.GetObject(); hBindings._flags = SQOBJ_FLAG_IMMUTABLE; if (SQ_FAILED(sq_translateasttobytecode(sqvm, ast, &hBindings, &buf[0], buf.size(), compilationOptions.raiseError))) { sq_releaseASTData(sqvm, ast); if (return_ast) *return_ast = nullptr; return false; } if (compilationOptions.doStaticAnalysis && !return_ast) { sq_analyzeast(sqvm, ast, &hBindings, buf.data(), buf.size()); } if (onBytecode_cb) { HSQOBJECT func; sq_getstackobj(sqvm, -1, &func); onBytecode_cb(sqvm, func, up_data); } if (!return_ast) sq_releaseASTData(sqvm, ast); return true; } bool SqModules::compileScript(const sqm::span buf, const string &resolved_fn, const char *requested_fn, Sqrat::Table &bindings, Sqrat::Object &script_closure, string &out_err_msg, SQCompilation::SqASTData **return_ast) { script_closure.Release(); string compileErrMsg; if (!compileScriptImpl(buf, resolved_fn.c_str(), bindings, compileErrMsg, return_ast)) { out_err_msg = format_string("Failed to compile file %s (%s)", requested_fn, resolved_fn.c_str()); if (!compileErrMsg.empty()) append_format(out_err_msg, ": %s", compileErrMsg.c_str()); return false; } script_closure = Sqrat::Var(sqvm, -1).value; sq_pop(sqvm, 1); return true; } Sqrat::Table SqModules::setupStateStorage(const char *resolved_fn) { SQRAT_ASSERT(sqvm); for (const Module &prevMod : prevModules) if (prevMod.fn == resolved_fn) return prevMod.stateStorage; return Sqrat::Table(sqvm); } SqModules::Module *SqModules::findModule(const char *resolved_fn) { for (Module &m : modules) if (m.fn == resolved_fn) return &m; return nullptr; } void SqModules::bindBaseLib(HSQOBJECT bindings) { sq_pushobject(sqvm, bindings); SQRAT_VERIFY(SQ_SUCCEEDED(sq_registerbaselib(sqvm))); sq_poptop(sqvm); } void SqModules::bindRequireApi(HSQOBJECT bindings) { sq_pushobject(sqvm, bindings); sq_pushstring(sqvm, "require", 7); sq_pushuserpointer(sqvm, this); sq_newclosure(sqvm, sqRequire, 1); sq_setnativeclosurename(sqvm, -1, "require"); SQRAT_VERIFY(SQ_SUCCEEDED(sq_setparamscheck(sqvm, 2, ".s"))); SQRAT_VERIFY(SQ_SUCCEEDED(sq_rawset(sqvm, -3))); sq_pushstring(sqvm, "require_optional", 16); sq_pushuserpointer(sqvm, this); sq_newclosure(sqvm, sqRequire, 1); sq_setnativeclosurename(sqvm, -1, "require_optional"); SQRAT_VERIFY(SQ_SUCCEEDED(sq_setparamscheck(sqvm, 2, ".s"))); SQRAT_VERIFY(SQ_SUCCEEDED(sq_rawset(sqvm, -3))); sq_poptop(sqvm); // bindings } void SqModules::bindModuleApi(HSQOBJECT bindings, Sqrat::Table &state_storage, Sqrat::Array &ref_holder, const char *__name__, const char *__filename__) { SQRAT_ASSERT(__name__); SQRAT_ASSERT(__filename__); HSQUIRRELVM vm = sqvm; sq_pushobject(vm, bindings); sq_pushstring(vm, "persist", 7); sq_pushobject(vm, state_storage.GetObject()); sq_newclosure(vm, persist_state, 1); sq_setnativeclosurename(vm, -1, "persist"); SQRAT_VERIFY(SQ_SUCCEEDED(sq_setparamscheck(vm, 3, ".sc"))); SQRAT_VERIFY(SQ_SUCCEEDED(sq_rawset(vm, -3))); sq_pushstring(vm, "keepref", 7); sq_pushobject(vm, ref_holder.GetObject()); sq_newclosure(vm, keepref, 1); sq_setnativeclosurename(vm, -1, "keepref"); SQRAT_VERIFY(SQ_SUCCEEDED(sq_rawset(vm, -3))); sq_pushstring(vm, "__name__", 8); sq_pushstring(vm, __name__, -1); SQRAT_VERIFY(SQ_SUCCEEDED(sq_rawset(vm, -3))); sq_pushstring(vm, "__filename__", 12); sq_pushstring(vm, __filename__, -1); SQRAT_VERIFY(SQ_SUCCEEDED(sq_rawset(vm, -3))); sq_pushstring(vm, "__static_analysis__", 19); sq_pushbool(vm, compilationOptions.doStaticAnalysis); SQRAT_VERIFY(SQ_SUCCEEDED(sq_rawset(vm, -3))); sq_pop(vm, 1); // bindings table } bool SqModules::mergeBindings(Sqrat::Table &target, Sqrat::Object &key, Sqrat::Object &value, string &out_err_msg) { if (target.RawHasKey(key)) { string keyStr = key.Cast(); Sqrat::Object current = target.RawGetSlot(key); if (value.IsEqual(current)) return true; else { out_err_msg = format_string("Duplicate field '%s' in imported bindings", keyStr.c_str()); return false; } } else { target.SetValue(key, value); return true; } } bool SqModules::mergeBindings(Sqrat::Table &target, Sqrat::Table &upd, string &out_err_msg) { Sqrat::Object::iterator it; while (upd.Next(it)) { Sqrat::Object key(it.getKey(), sqvm); Sqrat::Object val(it.getValue(), sqvm); if (!mergeBindings(target, key, val, out_err_msg)) return false; } return true; } bool SqModules::importModules(const char *resolved_fn, Sqrat::Table &bindings_dest, const SQModuleImport *imports, int num_imports, string &out_err_msg) { if (!num_imports) return true; size_t rsIdx = runningScripts.size(); runningScripts.emplace_back(resolved_fn); bool success = true; string mergeErrorMsg, requireErrorMsg; for (int iImp = 0; iImp < num_imports; ++iImp) { const SQModuleImport &import = imports[iImp]; Sqrat::Object exports; string moduleFnStr(import.name); if (beforeImportModuleCb && !beforeImportModuleCb(this, moduleFnStr.c_str())) { out_err_msg = format_string("%s:%d:%d: Module '%s' is forbidden", resolved_fn, import.line, import.nameColumn, moduleFnStr.c_str()); success = false; break; } auto nativeIt = nativeModules.find(import.name); if (nativeIt != nativeModules.end()) exports = nativeIt->second; else if (!requireModule(moduleFnStr.c_str(), true, __fn__, exports, requireErrorMsg)) { out_err_msg = format_string("Import error at %s:%d:%d: %s", resolved_fn, import.line, import.nameColumn, requireErrorMsg.c_str()); success = false; break; } if (import.numSlots == 0) // import module as slot { Sqrat::Object key(import.alias ? import.alias : import.name, sqvm); if (!mergeBindings(bindings_dest, key, exports, mergeErrorMsg)) { success = false; out_err_msg = format_string("%s:%d:%d: %s", resolved_fn, import.line, import.nameColumn, mergeErrorMsg.c_str()); } } else // import fields { if (exports.GetType() != OT_TABLE && exports.GetType() != OT_CLASS) { out_err_msg = format_string("%s:%d:%d: module '%s' export is not a table or class", resolved_fn, import.line, import.nameColumn, moduleFnStr.c_str()); success = false; break; } Sqrat::Table exportsTbl(exports); for (int iSlot = 0; iSlot < import.numSlots; ++iSlot) { const SQModuleImportSlot &slot = import.slots[iSlot]; if (strcmp(slot.name, "*") == 0) // import all fields { if (!mergeBindings(bindings_dest, exportsTbl, mergeErrorMsg)) { out_err_msg = format_string("%s:%d: %s", resolved_fn, import.line, mergeErrorMsg.c_str()); success = false; break; } } else // import single field { Sqrat::Object srcKey(slot.name, sqvm); if (!exportsTbl.RawHasKey(srcKey)) { out_err_msg = format_string("%s:%d:%d no field '%s' in module '%s' exports", resolved_fn, slot.line, slot.column, sq_objtostring(&srcKey.GetObject()), moduleFnStr.c_str()); success = false; break; } else { Sqrat::Object destKey(slot.alias ? slot.alias : slot.name, sqvm); Sqrat::Object val = exports.GetSlot(srcKey); if (!mergeBindings(bindings_dest, destKey, val, mergeErrorMsg)) { out_err_msg = format_string("%s:%d:%d: %s", resolved_fn, slot.line, slot.column, mergeErrorMsg.c_str()); success = false; break; } } } } } if (!success) break; } G_UNUSED(rsIdx); SQRAT_ASSERT(runningScripts.size() == rsIdx + 1); runningScripts.pop_back(); return success; } bool SqModules::requireModule(const char *requested_fn, bool must_exist, const char *__name__, Sqrat::Object &exports, string &out_err_msg) { out_err_msg.clear(); exports.Release(); string resolvedFn; fileAccess->resolveFileName(requested_fn, runningScripts.empty() ? nullptr : runningScripts.back().c_str(), resolvedFn); if (!checkCircularReferences(resolvedFn, requested_fn, out_err_msg)) return false; if (beforeImportModuleCb && !beforeImportModuleCb(this, requested_fn)) { out_err_msg = format_string("Module '%s' is forbidden", requested_fn); return false; } if (Module *found = findModule(resolvedFn.c_str())) { exports = found->exports; return true; } vector fileContents; bool readOk = fileAccess->readFile(resolvedFn, requested_fn, fileContents, out_err_msg); if (!readOk) return !must_exist; HSQUIRRELVM vm = sqvm; SQInteger prevTop = sq_gettop(sqvm); G_UNUSED(prevTop); SQRAT_ASSERT(__fn__ == nullptr); if (__name__ == __fn__) __name__ = resolvedFn.c_str(); Sqrat::Table stateStorage = setupStateStorage(resolvedFn.c_str()); Sqrat::Array refHolder(vm); Sqrat::Table bindingsTbl(vm); HSQOBJECT hBindings = bindingsTbl.GetObject(); bindBaseLib(hBindings); bindModuleApi(hBindings, stateStorage, refHolder, __name__, resolvedFn.c_str()); bindRequireApi(hBindings); SQRAT_ASSERT(sq_gettop(vm) == prevTop); sqm::span sourceCode = skip_bom(sqm::make_span(fileContents)); if (compilationOptions.doStaticAnalysis) sq_checktrailingspaces(vm, resolvedFn.c_str(), sourceCode.data(), sourceCode.size()); SQCompilation::SqASTData *astData = nullptr; Sqrat::Object scriptClosure; bool compileRes = compileScript(sourceCode, resolvedFn, requested_fn, bindingsTbl, scriptClosure, out_err_msg, compilationOptions.doStaticAnalysis ? &astData : nullptr); if (!compileRes) { sq_releaseASTData(vm, astData); SQRAT_ASSERT(sq_gettop(vm) == prevTop); return false; } size_t rsIdx = runningScripts.size(); G_UNUSED(rsIdx); runningScripts.emplace_back(resolvedFn.c_str()); sq_pushobject(vm, scriptClosure.GetObject()); sq_pushnull(vm); // module 'this' runningScriptClosuresStack.push_back(scriptClosure); SQRESULT callRes = sq_call(vm, 1, true, true); runningScriptClosuresStack.pop_back(); if (SQ_FAILED(callRes)) { SQRAT_ASSERT(runningScripts.size() == rsIdx + 1); runningScripts.pop_back(); out_err_msg = format_string("Failed to run script %s (%s)", requested_fn, resolvedFn.c_str()); sq_pop(vm, 1); // clojure, no return value on error sq_releaseASTData(vm, astData); SQRAT_ASSERT(sq_gettop(vm) == prevTop); return false; } HSQOBJECT hExports; sq_getstackobj(vm, -1, &hExports); exports = Sqrat::Object(hExports, vm); sq_pop(vm, 2); // retval + closure SQRAT_ASSERT(sq_gettop(vm) == prevTop); Module module; module.exports = exports; module.fn = resolvedFn; module.stateStorage = stateStorage; module.refHolder = refHolder; module.scriptClosure = scriptClosure; module.__name__ = __name__; modules.push_back(module); if (compilationOptions.doStaticAnalysis && astData) sq_analyzeast(vm, astData, &hBindings, sourceCode.data(), sourceCode.size()); SQRAT_ASSERT(runningScripts.size() == rsIdx + 1); runningScripts.pop_back(); sq_releaseASTData(vm, astData); return true; } bool SqModules::reloadModule(const char *fn, bool must_exist, const char *__name__, Sqrat::Object &exports, string &out_err_msg) { SQRAT_ASSERT(prevModules.empty()); modules.swap(prevModules); modules.clear(); // just in case callAndClearUnloadHandlers(false); string errMsg; bool res = requireModule(fn, must_exist, __name__, exports, out_err_msg); prevModules.clear(); return res; } bool SqModules::reloadAll(string &full_err_msg) { SQRAT_ASSERT(prevModules.empty()); full_err_msg.clear(); modules.swap(prevModules); modules.clear(); // just in case callAndClearUnloadHandlers(false); bool res = true; Sqrat::Object exportsTmp; string errMsg; for (const Module &prev : prevModules) { if (!requireModule(prev.fn.c_str(), true, prev.__name__.c_str(), exportsTmp, errMsg)) { res = false; append_format(full_err_msg, "%s: %s\n", prev.fn.c_str(), errMsg.c_str()); } } prevModules.clear(); return res; } bool SqModules::addNativeModule(const char *module_name, const Sqrat::Object &exports, const char *module_doc_string) { if (!module_name || !*module_name) { SQRAT_ASSERT(0); return false; } auto ins = nativeModules.insert({string(module_name), exports}); SQRAT_ASSERT(ins.second && "Module registered twice"); if (module_doc_string && ins.second) sq_setobjectdocstring(exports.GetVM(), &const_cast(exports).GetObject(), module_doc_string); return ins.second; // false if already registered } Sqrat::Array SqModules::getNativeModuleNames() { Sqrat::Array list(sqvm, nativeModules.size()); SQInteger idx = 0; for (auto &nm : nativeModules) { list.SetValue(idx, nm.first.data()); ++idx; } return list; } void SqModules::getLoadedModules(vector &file_names) { file_names.clear(); file_names.reserve(modules.size()); for (const Module &m : modules) file_names.push_back(m.fn); } template SQInteger SqModules::sqRequire(HSQUIRRELVM vm) // arguments: fileName, mustExist=true { SQUserPointer selfPtr = nullptr; if (SQ_FAILED(sq_getuserpointer(vm, -1, &selfPtr)) || !selfPtr) return sq_throwerror(vm, "No module manager"); SqModules *self = reinterpret_cast(selfPtr); SQRAT_ASSERT(self->sqvm == vm); const char *fileName = nullptr; SQInteger fileNameLen = -1; sq_getstringandsize(vm, 2, &fileName, &fileNameLen); if (strcmp(fileName, "quirrel.native_modules") == 0) { Sqrat::Array list = self->getNativeModuleNames(); sq_pushobject(vm, list); return 1; } if (self->beforeImportModuleCb && !self->beforeImportModuleCb(self, fileName)) return sqstd_throwerrorf(vm, "Module '%s' is forbidden", fileName); bool searchNative = true, searchScript = true; self->fileAccess->getSearchTargets(fileName, searchNative, searchScript); auto nativeIt = searchNative ? self->nativeModules.find(fileName) : self->nativeModules.end(); if (nativeIt != self->nativeModules.end()) { sq_pushobject(vm, nativeIt->second); return 1; } if (!searchScript) { if (!must_exist) { sq_pushnull(vm); return 1; } else return sqstd_throwerrorf(vm, "Script module '%s' not found", fileName); } Sqrat::Object exports(vm); string errMsg; if (!self->requireModule(fileName, must_exist, __fn__, exports, errMsg)) return sq_throwerror(vm, errMsg.c_str()); sq_pushobject(vm, exports.GetObject()); return 1; } void SqModules::registerStdLibNativeModule(const char *name, RegFunc reg_func) { HSQOBJECT hModule; sq_newtable(sqvm); sq_getstackobj(sqvm, -1, &hModule); reg_func(sqvm); bool regRes = addNativeModule(name, Sqrat::Object(hModule, sqvm)); G_UNUSED(regRes); SQRAT_ASSERT(regRes); sq_pop(sqvm, 1); } void SqModules::registerTypesLib() { registerStdLibNativeModule("types", sq_registertypeslib); } void SqModules::registerMathLib() { registerStdLibNativeModule("math", sqstd_register_mathlib); } void SqModules::registerStringLib() { registerStdLibNativeModule("string", sqstd_register_stringlib); } void SqModules::registerSystemLib() { registerStdLibNativeModule("system", sqstd_register_systemlib); } void SqModules::registerDateTimeLib() { registerStdLibNativeModule("datetime", sqstd_register_datetimelib); } void SqModules::registerDebugLib() { registerStdLibNativeModule("debug", sqstd_register_debuglib); } void SqModules::registerIoStreamLib() { HSQOBJECT hModule; sq_resetobject(&hModule); sq_newtable(sqvm); SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(sqvm, -1, &hModule))); SQRAT_VERIFY(SQ_SUCCEEDED(sqstd_init_streamclass(sqvm))); SQRAT_VERIFY(SQ_SUCCEEDED(sqstd_register_bloblib(sqvm))); bool regRes = addNativeModule("iostream", Sqrat::Object(hModule, sqvm)); G_UNUSED(regRes); SQRAT_ASSERTF(regRes, "Failed to init 'iostream' library with module manager"); sq_pop(sqvm, 1); } void SqModules::registerIoLib() { if (!findNativeModule("iostream")) { // register 'iostream' module containing basestream class and blob registerIoStreamLib(); } registerStdLibNativeModule("io", sqstd_register_iolib); } void SqModules::callAndClearUnloadHandlers(bool is_closing) { resetStaticMemos(); for (const Sqrat::Object &f : onModuleUnload) { SQInteger nparams = 0, nfreevars = 0; sq_pushobject(sqvm, f.GetObject()); SQRAT_VERIFY(SQ_SUCCEEDED(sq_getclosureinfo(sqvm, -1, &nparams, &nfreevars))); sq_pop(sqvm, 1); Sqrat::Function func(sqvm, Sqrat::Object(), f.GetObject()); if (nparams == 1) func(); else if (nparams == 2) func(is_closing); else SQRAT_ASSERT(0); } onModuleUnload.clear(); } SQInteger SqModules::register_on_module_unload(HSQUIRRELVM vm) { SQInteger nparams = 0, nfreevars = 0; SQRAT_VERIFY(SQ_SUCCEEDED(sq_getclosureinfo(vm, 2, &nparams, &nfreevars))); if (nparams != 1 && nparams != 2) return sqstd_throwerrorf(vm, "Function accepts 'this' + optional argument, but provided function requires %d args", nparams); SqModules *self = nullptr; SQRAT_VERIFY(SQ_SUCCEEDED(sq_getuserpointer(vm, 3, (SQUserPointer *)&self))); HSQOBJECT hFunc; sq_getstackobj(vm, 2, &hFunc); auto it = SQRAT_STD::find_if(self->onModuleUnload.begin(), self->onModuleUnload.end(), [hFunc](const Sqrat::Object &f) { return f.GetType() == sq_type(hFunc) && f.GetObject()._unVal.raw == hFunc._unVal.raw; }); if (it == self->onModuleUnload.end()) self->onModuleUnload.push_back(Sqrat::Object(hFunc, vm)); return 0; } void SqModules::resetStaticMemos() { for (Module &module : modules) sq_reset_static_memos(sqvm, module.scriptClosure.GetObject()); for (Module &module : prevModules) sq_reset_static_memos(sqvm, module.scriptClosure.GetObject()); for (Sqrat::Object &f : runningScriptClosuresStack) sq_reset_static_memos(sqvm, f.GetObject()); } void SqModules::registerModulesLib() { Sqrat::Table exports(sqvm); sq_pushuserpointer(sqvm, this); Sqrat::Var selfObj(sqvm, -1); sq_pop(sqvm, 1); exports .Func( "get_native_module_names", [this]() { return this->getNativeModuleNames(); }, "Returns an array with a list of names of native modules.") .Func( "reset_static_memos", [this]() { return this->resetStaticMemos(); }, "Reset static memo expressions cache.") .SquirrelFunc("on_module_unload", register_on_module_unload, 2, ".c", "Register module unload callback. " "Example: on_module_unload( function(is_app_closing) {println(is_app_closing ? \"Closing\" : \"Soft reloading\")} )", 1, &selfObj.value); addNativeModule("modules", exports, "Contains functions to work with modules, like on_module_unload(), get_native_module_names()"); } ================================================ FILE: sqmodules/sqmodules.h ================================================ #pragma once #include #include #if defined(SQRAT_HAS_EASTL) #include #include #include #else #include #include #include #include #endif #include "span.h" namespace SQCompilation { struct SqASTData; } struct ISqModulesFileAccess { template using vector = SQRAT_STD::vector; using string = Sqrat::string; virtual ~ISqModulesFileAccess() {} virtual void destroy() = 0; virtual void resolveFileName(const char *requested_fn, const char *running_script, string &res) = 0; // when file can't be read, return false and fill out_err_msg virtual bool readFile(const string &resolved_fn, const char *requested_fn, vector &buf, string &out_err_msg) = 0; virtual void getSearchTargets(const char * /*fn*/, bool &search_native, bool &search_script) { search_native = true; search_script = true; } }; struct DefSqModulesFileAccess : public ISqModulesFileAccess { virtual void destroy() override; virtual void resolveFileName(const char *requested_fn, const char *running_script, string &res) override; // when file can't be read, return false and fill out_err_msg virtual bool readFile(const string &resolved_fn, const char *requested_fn, vector &buf, string &out_err_msg) override; string makeRelativeFilename(const char *cur_file, const char *requested_fn); bool useAbsolutePath = false; vector scriptPathBuf; // cache bool needToDeleteSelf = false; }; class SqModules { private: SqModules(const SqModules &) = delete; SqModules(SqModules &&) = delete; public: template using vector = SQRAT_STD::vector; using string = Sqrat::string; typedef bool (*BeforeImportModuleCallback)(SqModules *sq_modules, const char *module_name); typedef void (*SQOnCompileFileCb)(HSQUIRRELVM, const char *); struct Module { string fn; // filename resolved to the full path Sqrat::Object exports; Sqrat::Object stateStorage; Sqrat::Array refHolder; Sqrat::Object scriptClosure; string __name__; }; SqModules(HSQUIRRELVM vm, ISqModulesFileAccess *file_access); ~SqModules(); HSQUIRRELVM getVM() { return sqvm; } // File name may be: // 1) relative to currently running script // 2) relative to base path // Note: accessing files outside of current base paths (via ../../) // can mess things up (breaking load module once rule) // __name__ is put to module this. Use __fn__ constant to use resolved filename or custom string to override bool requireModule(const char *fn, bool must_exist, const char *__name__, Sqrat::Object &exports, string &out_err_msg); // This can also be used for initial module execution bool reloadModule(const char *fn, bool must_exist, const char *__name__, Sqrat::Object &exports, string &out_err_msg); bool reloadAll(string &err_msg); void resetStaticMemos(); bool addNativeModule(const char *module_name, const Sqrat::Object &exports, const char *module_doc_string = nullptr); void registerTypesLib(); void registerMathLib(); void registerStringLib(); void registerSystemLib(); void registerIoStreamLib(); void registerIoLib(); void registerDateTimeLib(); void registerDebugLib(); void registerModulesLib(); template void forEachNativeModule(const F &cb); // 'cb' called as cb(const char *module_name, const Sqrat::Object &module) Sqrat::Object *findNativeModule(const char *module_name); void getLoadedModules(vector &file_names); void setModuleImportCallback(BeforeImportModuleCallback before_import_module_callback) { beforeImportModuleCb = before_import_module_callback; } void bindRequireApi(HSQOBJECT bindings); void bindModuleApi(HSQOBJECT bindings, Sqrat::Table &state_storage, Sqrat::Array &ref_holder, const char *__name__, const char *__filename__); void bindBaseLib(HSQOBJECT bindings); private: // Script API // require(file_name, must_exist=true) template static SQInteger sqRequire(HSQUIRRELVM vm); void resolveFileName(const char *fn, string &res); bool checkCircularReferences(const string &resolved_fn, const char *orig_fn, string &out_err_msg); bool readFile(const string &resolved_fn, const char *requested_fn, vector &buf, string &out_err_msg); bool compileScript(const sqm::span buf, const string &resolved_fn, const char *orig_fn, Sqrat::Table &bindings, Sqrat::Object &script_closure, string &out_err_msg, SQCompilation::SqASTData **return_ast = nullptr); bool compileScriptImpl(const sqm::span buf, const char *resolved_fn, Sqrat::Table &bindings, string &out_err_msg, SQCompilation::SqASTData **return_ast = nullptr); Sqrat::Table setupStateStorage(const char *resolved_fn); Module *findModule(const char *resolved_fn); typedef SQInteger (*RegFunc)(HSQUIRRELVM); void registerStdLibNativeModule(const char *name, RegFunc); Sqrat::Array getNativeModuleNames(); BeforeImportModuleCallback beforeImportModuleCb; bool mergeBindings(Sqrat::Table &target, Sqrat::Object &key, Sqrat::Object &value, string &out_err_msg); bool mergeBindings(Sqrat::Table &target, Sqrat::Table &upd, string &out_err_msg); bool importModules(const char *resolved_fn, Sqrat::Table &bindings_dest, const SQModuleImport *imports, int num_imports, string &out_err_msg); static SQInteger register_on_module_unload(HSQUIRRELVM vm); void callAndClearUnloadHandlers(bool is_closing); public: static const char *__main__, *__fn__, *__analysis__; struct { bool raiseError; bool doStaticAnalysis; } compilationOptions; void *up_data; void (*onAST_cb)(HSQUIRRELVM, SQCompilation::SqASTData *, void *); void (*onBytecode_cb)(HSQUIRRELVM, HSQOBJECT, void *); SQOnCompileFileCb onCompileFile_cb = nullptr; private: vector modules; vector prevModules; //< for hot reload vector runningScriptClosuresStack; #if defined(SQRAT_HAS_EASTL) using NativeModulesMap = eastl::vector_map; #else using NativeModulesMap = std::unordered_map; #endif NativeModulesMap nativeModules; vector runningScripts; vector onModuleUnload; HSQUIRRELVM sqvm = nullptr; ISqModulesFileAccess *fileAccess = nullptr; }; template inline void SqModules::forEachNativeModule(const F &cb) { for (auto &nm : nativeModules) cb(nm.first.data(), nm.second); } inline Sqrat::Object *SqModules::findNativeModule(const char *module_name) { auto it = nativeModules.find(module_name); return (it == nativeModules.end()) ? nullptr : &it->second; } ================================================ FILE: sqrat/README.txt ================================================ Sqrat - Quirrel Binding Utility © 2009 Brandon Jones © 2011-2014 Li-Cheng (Andy) Tai © 2013-2015 Brandon Haffen AKA Wizzard © 2016-2019 Gaijin Entertainment Sqrat is a C++ binding utility for the quirrel language. ================================================ FILE: sqrat/include/sqrat/sqratAllocator.h ================================================ // Sqrat: altered version by Gaijin Games KFT // SqratAllocator: Custom Class Allocation/Deallocation // // // Copyright (c) 2009 Brandon Jones // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // #pragma once #if !defined(_SQRAT_ALLOCATOR_H_) #define _SQRAT_ALLOCATOR_H_ #include #include #include #include "sqratObject.h" #include "sqratTypes.h" namespace Sqrat { namespace vargs { template T& extract(Var& var) { return var.value; } template T extract(T&& var) { return var; } template C *apply_ctor_helper(SQRAT_STD::index_sequence, Tuple &&args) { (void)args; // 'args' is unused in case of empty 'Indexes' return new C(extract(SQRAT_STD::get(args))...); } template C *apply_ctor(Tuple &&tup) { constexpr auto argsN = SQRAT_STD::tuple_size>::value; return apply_ctor_helper(SQRAT_STD::make_index_sequence(), tup); } template void apply_placement_ctor_helper(void *mem, SQRAT_STD::index_sequence, Tuple &&args) { (void)args; new (mem) C(extract(SQRAT_STD::get(args))...); } template void apply_placement_ctor(void *mem, Tuple &&tup) { constexpr auto argsN = SQRAT_STD::tuple_size>::value; apply_placement_ctor_helper(mem, SQRAT_STD::make_index_sequence(), tup); } } template struct NewC { T* p; NewC() { p = new T(); } }; template struct NewC { T* p; NewC() { p = 0; } }; template class DefaultAllocator { public: static SQInteger New(HSQUIRRELVM vm) { if constexpr (SQRAT_STD::is_default_constructible::value && sizeof(C) <= 256 && alignof(C) <= SQ_ALIGNMENT) { C* ptr = nullptr; sq_getinstanceup(vm, 1, (SQUserPointer*)&ptr, nullptr); if (ptr) { new (ptr) C(); sq_setreleasehook(vm, 1, &ClassType::DestructInline); return 0; } } ClassType::SetManagedInstance(vm, 1, NewC::value >().p); return 0; } static SQInteger iNew(HSQUIRRELVM vm) { return New(vm); } template static SQInteger iNew(HSQUIRRELVM vm) { if (!vargs::check_var_types(vm, 2)) return SQ_ERROR; auto vars = vargs::make_vars(vm, 2); if constexpr (sizeof(C) <= 256 && alignof(C) <= SQ_ALIGNMENT) { C* ptr = nullptr; sq_getinstanceup(vm, 1, (SQUserPointer*)&ptr, nullptr); if (ptr) { vargs::apply_placement_ctor(ptr, SQRAT_STD::move(vars)); sq_setreleasehook(vm, 1, &ClassType::DestructInline); return 0; } } C *inst = vargs::apply_ctor(vars); ClassType::SetManagedInstance(vm, 1, inst); return 0; } public: // Copy using inline userdata (placement new into SQInstance's inline space) // or heap allocation as fallback for large types. static SQInteger Copy(HSQUIRRELVM vm, SQInteger idx, const void* value) { if constexpr (sizeof(C) <= 256 && alignof(C) <= SQ_ALIGNMENT) { // Inline path: _userpointer already points to inline space from sq_createinstance C* ptr = nullptr; sq_getinstanceup(vm, idx, (SQUserPointer*)&ptr, nullptr); if (ptr) { new (ptr) C(*static_cast(value)); sq_setreleasehook(vm, idx, &ClassType::DestructInline); return 0; } } // Fallback: heap allocation ClassType::SetManagedInstance(vm, idx, new C(*static_cast(value))); return 0; } }; template class NoConstructor { public: static SQInteger New(HSQUIRRELVM vm) { return sqstd_throwerrorf(vm, "Construction of %s is not allowed", ClassType::ClassName().c_str()); } static SQInteger Copy(HSQUIRRELVM vm, SQInteger idx, const void* value) { SQRAT_UNUSED(vm); SQRAT_UNUSED(idx); SQRAT_UNUSED(value); return sqstd_throwerrorf(vm, "Cloning of %s is not allowed", ClassType::ClassName().c_str()); } }; template class CopyOnly { public: static SQInteger New(HSQUIRRELVM vm) { return sqstd_throwerrorf(vm, "Construction of %s is not allowed", ClassType::ClassName().c_str()); } static SQInteger Copy(HSQUIRRELVM vm, SQInteger idx, const void* value) { if constexpr (sizeof(C) <= 256 && alignof(C) <= SQ_ALIGNMENT) { C* ptr = nullptr; sq_getinstanceup(vm, idx, (SQUserPointer*)&ptr, nullptr); if (ptr) { new (ptr) C(*static_cast(value)); sq_setreleasehook(vm, idx, &ClassType::DestructInline); return 0; } } ClassType::SetManagedInstance(vm, idx, new C(*static_cast(value))); return 0; } }; template class NoCopy { public: static SQInteger New(HSQUIRRELVM vm) { ClassType::SetManagedInstance(vm, 1, NewC::value >().p); return 0; } static SQInteger iNew(HSQUIRRELVM vm) { return New(vm); } template static SQInteger iNew(HSQUIRRELVM vm) { if (!vargs::check_var_types(vm, 2)) return SQ_ERROR; auto vars = vargs::make_vars(vm, 2); C *inst = vargs::apply_ctor(vars); ClassType::SetManagedInstance(vm, 1, inst); return 0; } static SQInteger Copy(HSQUIRRELVM vm, SQInteger idx, const void* value) { SQRAT_UNUSED(vm); SQRAT_UNUSED(idx); SQRAT_UNUSED(value); return sqstd_throwerrorf(vm, "Cloning of %s is not allowed", ClassType::ClassName().c_str()); } }; } #endif ================================================ FILE: sqrat/include/sqrat/sqratArray.h ================================================ // Sqrat: altered version by Gaijin Games KFT // SqratArray: Array Binding // // // Copyright 2011 Alston Chen // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // #pragma once #if !defined(_SQRAT_ARRAY_H_) #define _SQRAT_ARRAY_H_ #include #include #include "sqratObject.h" #include "sqratFunction.h" #include "sqratGlobalMethods.h" namespace Sqrat { class ArrayBase : public Object { public: ArrayBase() { } ArrayBase(HSQUIRRELVM v) : Object(v) { } ArrayBase(const Object& obj) : Object(obj) { } ArrayBase(HSQOBJECT o, HSQUIRRELVM v) : Object(o, v) { } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Binds a Table or Class to the specific element of Array (can be used to facilitate namespaces) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// void Bind(const SQInteger index, Object& object) { sq_pushobject(vm, GetObject()); sq_pushinteger(vm, index); sq_pushobject(vm, object.GetObject()); sq_set(vm, -3); sq_pop(vm,1); // pop array } template ArrayBase& SetValue(const SQInteger index, const V& val) { sq_pushobject(vm, GetObject()); sq_pushinteger(vm, index); PushVar(vm, val); sq_set(vm, -3); sq_pop(vm,1); // pop array return *this; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Sets an index in the Array to a specific instance (like a reference) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template ArrayBase& SetInstance(const SQInteger index, V* val) { BindInstance(index, val, false); return *this; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Sets an index in the Array to a specific function ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template ArrayBase& Func(const SQInteger index, F method) { BindFunc(index, method, SqGlobalThunk(), 1+SqGetArgCount()); return *this; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Returns the element at a given index ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template T GetValue(int index) { sq_pushobject(vm, obj); sq_pushinteger(vm, index); if (SQ_FAILED(sq_get(vm, -2))) { sq_pop(vm, 1); SQRAT_ASSERT(0); // Ensure that index is valid before calling this method return T(); } Var element(vm, -1); sq_pop(vm, 2); return element.value; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Gets a Function from an index in the Array ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Function GetFunction(const SQInteger index) { HSQOBJECT funcObj; sq_pushobject(vm, GetObject()); sq_pushinteger(vm, index); if(SQ_FAILED(sq_get(vm, -2))) { sq_pop(vm, 1); return Function(); } SQObjectType value_type = sq_gettype(vm, -1); if (value_type != OT_CLOSURE && value_type != OT_NATIVECLOSURE) { sq_pop(vm, 2); return Function(); } SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm, -1, &funcObj))); Function ret(vm, GetObject(), funcObj); // must addref before the pop! sq_pop(vm, 2); return ret; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Fills a C array with the elements of the Array /// /// \param array C array to be filled /// \param size The amount of elements to fill the C array with /// /// \tparam T Type of elements (fails if any elements in Array are not of this type) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template void GetArray(T* array, int size) { HSQOBJECT value = GetObject(); sq_pushobject(vm, value); // Before calling this method ensure that size provided size matches array's one SQRAT_ASSERTF(size==sq_getsize(vm, -1), "array size mismatch (%d vs %d)", size, sq_getsize(vm, -1)); sq_pushnull(vm); SQInteger i; while (SQ_SUCCEEDED(sq_next(vm, -2))) { sq_getinteger(vm, -2, &i); if (i >= size) break; Var element(vm, -1); // TODO: handle error sq_pop(vm, 2); array[i] = element.value; } sq_pop(vm, 2); // pops the null iterator and the array object } template ArrayBase& Append(const V& val) { sq_pushobject(vm, GetObject()); PushVar(vm, val); sq_arrayappend(vm, -2); sq_pop(vm,1); // pop array return *this; } template ArrayBase& Append(V* val) { sq_pushobject(vm, GetObject()); PushVar(vm, val); sq_arrayappend(vm, -2); sq_pop(vm,1); // pop array return *this; } template ArrayBase& Insert(const SQInteger destpos, const V& val) { sq_pushobject(vm, GetObject()); PushVar(vm, val); sq_arrayinsert(vm, -2, destpos); sq_pop(vm,1); // pop array return *this; } template ArrayBase& Insert(const SQInteger destpos, V* val) { sq_pushobject(vm, GetObject()); PushVar(vm, val); sq_arrayinsert(vm, -2, destpos); sq_pop(vm,1); // pop array return *this; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Removes the last element from the Array and returns it (returns null if failed) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Object Pop() { HSQOBJECT slotObj; sq_pushobject(vm, GetObject()); if(SQ_FAILED(sq_arraypop(vm, -1, true))) { sq_pop(vm, 1); return Object(); // Return a NULL object } else { SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm, -1, &slotObj))); Object ret(slotObj, vm); sq_pop(vm, 2); return ret; } } ArrayBase& Remove(const SQInteger itemidx) { sq_pushobject(vm, GetObject()); sq_arrayremove(vm, -1, itemidx); sq_pop(vm,1); // pop array return *this; } ArrayBase& Resize(const SQInteger newsize) { sq_pushobject(vm, GetObject()); sq_arrayresize(vm, -1, newsize); sq_pop(vm,1); // pop array return *this; } ArrayBase& Reverse() { sq_pushobject(vm, GetObject()); sq_arrayreverse(vm, -1); sq_pop(vm,1); // pop array return *this; } SQInteger Length() const { sq_pushobject(vm, obj); SQInteger r = sq_getsize(vm, -1); sq_pop(vm, 1); return r; } bool Clear() { sq_pushobject(vm, GetObject()); bool ok = SQ_SUCCEEDED(sq_clear(vm, -1)); sq_pop(vm, 1); // pop array return ok; } }; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Represents an array in Squirrel ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class Array : public ArrayBase { public: Array() { } Array(HSQUIRRELVM v, const SQInteger size = 0) : ArrayBase(v) { SQRAT_ASSERT(v); sq_newarray(vm, size); SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm,-1,&obj))); sq_addref(vm, &obj); sq_pop(vm,1); } Array(const Object& obj) : ArrayBase(obj) { } Array(HSQOBJECT o, HSQUIRRELVM v) : ArrayBase(o, v) { } }; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Used to get and push Array instances to and from the stack as references (arrays are always references in Squirrel) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template<> struct Var { Array value; ///< The actual value of get operations ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Attempts to get the value off the stack at idx as an Array ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Var(HSQUIRRELVM vm, SQInteger idx) { HSQOBJECT obj; sq_resetobject(&obj); SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm,idx,&obj))); value = Array(obj, vm); SQObjectType value_type = sq_gettype(vm, idx); if (value_type != OT_ARRAY && value_type != OT_NULL) { SQRAT_ASSERTF(0, FormatTypeError(vm, idx, "array").c_str()); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Called by Sqrat::PushVar to put an Array reference on the stack ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// static void push(HSQUIRRELVM vm, const Array& value) { HSQOBJECT obj; sq_resetobject(&obj); obj = value.GetObject(); sq_pushobject(vm,obj); } static const char * getVarTypeName() { return "array"; } static bool check_type(HSQUIRRELVM vm, SQInteger idx) { return sq_gettype(vm, idx) == OT_ARRAY || sq_gettype(vm, idx) == OT_NULL; } }; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Used to get and push Array instances to and from the stack as references (arrays are always references in Squirrel) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template<> struct Var : Var {Var(HSQUIRRELVM vm, SQInteger idx) : Var(vm, idx) {}}; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Used to get and push Array instances to and from the stack as references (arrays are always references in Squirrel) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template<> struct Var : Var {Var(HSQUIRRELVM vm, SQInteger idx) : Var(vm, idx) {}}; } #endif ================================================ FILE: sqrat/include/sqrat/sqratClass.h ================================================ // Sqrat: altered version by Gaijin Games KFT // SqratClass: Class Binding // // // Copyright (c) 2009 Brandon Jones // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // #pragma once #if !defined(_SQRAT_CLASS_H_) #define _SQRAT_CLASS_H_ #include #include #include "sqratObject.h" #include "sqratClassType.h" #include "sqratMemberMethods.h" #include "sqratAllocator.h" #include "sqratTypes.h" namespace Sqrat { // Maps C++ types to SQNFT_* constants for sq_registernativefield. template struct NativeFieldTypeFor; template<> struct NativeFieldTypeFor { enum { value = SQNFT_FLOAT32 }; }; template<> struct NativeFieldTypeFor { enum { value = SQNFT_FLOAT64 }; }; template<> struct NativeFieldTypeFor { enum { value = SQNFT_INT32 }; }; template<> struct NativeFieldTypeFor { enum { value = SQNFT_INT64 }; }; template<> struct NativeFieldTypeFor { enum { value = SQNFT_BOOL }; }; template struct InstanceToString; /// Facilitates exposing a C++ class with no base class to Squirrel /// /// \tparam C Class type to expose /// \tparam A An allocator to use when instantiating and destroying class instances of this type in Squirrel /// /// \remarks /// DefaultAllocator is used if no allocator is specified. This should be sufficent for most classes, /// but if specific behavior is desired, it can be overridden. If the class should not be instantiated from /// Squirrel the NoConstructor allocator may be used. See NoCopy and CopyOnly too. template > class Class { protected: HSQUIRRELVM vm = nullptr; public: /// \param v Squirrel virtual machine to create the Class for /// \param className A necessarily unique name for the class that can appear in error messages /// \param createClass Should class type data be created? (almost always should be true - don't worry about it) Class(HSQUIRRELVM v, string && className, bool createClass = true) : vm(v) { if (createClass && !ClassType::hasClassData(v)) { // Register static TypeTag (once per type, first VM wins) TypeTag* tag = TypeTag::get(); TypeTagBase::allTypeTags().insert(tag); // Create per-VM ClassData ClassData* cd = new ClassData; cd->copyFunc = &A::Copy; cd->className = className; if (ClassData::staticClassName.empty()) { ClassData::staticClassName = className; tag->className = ClassData::staticClassName.c_str(); } ClassType::setClassData(v, cd); SqratCleanup::getOrCreate(v)->add(cd); HSQOBJECT& classObj = cd->classObj; //-V522 SQRAT_VERIFY(SQ_SUCCEEDED(sq_newclass(v, false))); SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(v, -1, &classObj))); sq_addref(v, &classObj); // must addref before the pop! sq_pop(v, 1); InitClass(cd); } } /// Gets the Squirrel object for this Class (copy) HSQOBJECT GetObject() const { return ClassType::getClassData(vm)->classObj; //-V522 } /// Gets the Squirrel object for this Class (reference) HSQOBJECT& GetObject() { return ClassType::getClassData(vm)->classObj; //-V522 } public: /// Binds a class variable of type V (getter + setter) template Class& Var(const char* name, V C::* var) { ClassData* cd = ClassType::getClassData(vm); BindAccessor(name, &var, sizeof(var), &sqDefaultGet, cd->getTable); //-V522 BindAccessor(name, &var, sizeof(var), &sqDefaultSet, cd->setTable); //-V522 return *this; } /// Binds a class variable without a setter (read-only) template Class& ConstVar(const char* name, V C::* var) { ClassData* cd = ClassType::getClassData(vm); BindAccessor(name, &var, sizeof(var), &sqDefaultGet, cd->getTable); //-V522 return *this; } /// Binds a native field for direct VM access (no metamethod dispatch). /// Only works for primitive types (float, double, int32_t, int64_t, bool) /// on classes with inline userdata. template Class& NativeVar(const char* name, V C::* member) { static_assert(sizeof(C) <= 256 && alignof(C) <= SQ_ALIGNMENT, "NativeVar requires type to fit in inline userdata"); ClassData* cd = ClassType::getClassData(vm); sq_pushobject(vm, cd->classObj); //-V522 alignas(C) char buf[sizeof(C)] = {}; C* dummy = reinterpret_cast(buf); SQInteger offset = (SQInteger)((const char*)&(dummy->*member) - (const char*)dummy); SQRESULT res = sq_registernativefield(vm, -1, name, offset, NativeFieldTypeFor::value); SQRAT_UNUSED(res); SQRAT_ASSERTF(SQ_SUCCEEDED(res), "NativeVar('%s') failed: call UseInlineUserdata() before registering native fields", name); sq_pop(vm, 1); return *this; } template Class& StaticVar(const char* name, V* var) { ClassData* cd = ClassType::getClassData(vm); BindAccessor(name, &var, sizeof(var), &sqStaticGet, cd->getTable); //-V522 BindAccessor(name, &var, sizeof(var), &sqStaticSet, cd->setTable); //-V522 return *this; } template Class& Prop(const char* name, F1 getMethod, F2 setMethod) { ClassData* cd = ClassType::getClassData(vm); if(getMethod != NULL) BindAccessor(name, &getMethod, sizeof(getMethod), SqMemberFunc(), cd->getTable); //-V522 if(setMethod != NULL) BindAccessor(name, &setMethod, sizeof(setMethod), SqMemberFunc(), cd->setTable); //-V522 return *this; } template Class& GlobalProp(const char* name, F1 getMethod, F2 setMethod) { ClassData* cd = ClassType::getClassData(vm); if(getMethod != NULL) BindAccessor(name, &getMethod, sizeof(getMethod), SqMemberGlobalThunk(), cd->getTable); //-V522 if(setMethod != NULL) BindAccessor(name, &setMethod, sizeof(setMethod), SqMemberGlobalThunk(), cd->setTable); //-V522 return *this; } template Class& Prop(const char* name, F getMethod) { BindAccessor(name, &getMethod, sizeof(getMethod), SqMemberFunc(), ClassType::getClassData(vm)->getTable); //-V522 return *this; } Class& SquirrelProp(const char* name, SQFUNCTION getMethod, const char *docstring=nullptr) { sq_pushobject(vm, ClassType::getClassData(vm)->getTable); //-V522 sq_pushstring(vm, name, -1); sq_newclosure(vm, getMethod, 0); SQRAT_VERIFY(SQ_SUCCEEDED(sq_setparamscheck(vm, 1, "x"))); if (docstring) SQRAT_VERIFY(SQ_SUCCEEDED(sq_setnativeclosuredocstring(vm, -1, docstring))); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, false))); sq_pop(vm, 1); return *this; } Class& SquirrelProp(const char* name, SQFUNCTION getMethod, SQFUNCTION setMethod, const char *docstring=nullptr) { sq_pushobject(vm, ClassType::getClassData(vm)->getTable); //-V522 sq_pushstring(vm, name, -1); sq_newclosure(vm, getMethod, 0); SQRAT_VERIFY(SQ_SUCCEEDED(sq_setparamscheck(vm, 1, "x"))); if (docstring) SQRAT_VERIFY(SQ_SUCCEEDED(sq_setnativeclosuredocstring(vm, -1, docstring))); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, false))); sq_pop(vm, 1); sq_pushobject(vm, ClassType::getClassData(vm)->setTable); sq_pushstring(vm, name, -1); sq_newclosure(vm, setMethod, 0); SQRAT_VERIFY(SQ_SUCCEEDED(sq_setparamscheck(vm, 2, "x."))); if (docstring) SQRAT_VERIFY(SQ_SUCCEEDED(sq_setnativeclosuredocstring(vm, -1, docstring))); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, false))); sq_pop(vm, 1); return *this; } /// Binds a read-only class property (using a global function instead of a member function) template Class& GlobalProp(const char* name, F getMethod) { BindAccessor(name, &getMethod, sizeof(getMethod), SqMemberGlobalThunk(), ClassType::getClassData(vm)->getTable); //-V522 return *this; } /// Binds a class function template Class& Func(const char* name, F method, const char *docstring=nullptr) { //-V1071 BindFunc(name, method, SqMemberFunc(), 1+SqGetArgCount(), false, docstring); return *this; } /// Binds a global function as a class function template Class& GlobalFunc(const char* name, F method, const char *docstring=nullptr) { //-V1071 BindFunc(name, method, SqMemberGlobalThunk(), SqGetArgCount(), false, docstring); return *this; } /// Binds a static class function template Class& StaticFunc(const char* name, F method, const char *docstring=nullptr) { //-V1071 BindFunc(name, method, SqGlobalThunk(), 1+SqGetArgCount(), false, docstring); return *this; } /// Binds a raw Squirrel function as a class function. /// The class instance will be at index 1, arguments after that. Class& SquirrelFunc(const char* name, SQFUNCTION func, SQInteger nparamscheck, const char *typemask=nullptr, const char *docstring=nullptr, SQInteger nfreevars=0, const Object *freevars=nullptr, //-V1071 FunctionPurity purity=FunctionPurity::SideEffects) { sq_pushobject(vm, ClassType::getClassData(vm)->classObj); //-V522 sq_pushstring(vm, name, -1); for (SQInteger i=0; i::getClassData(vm)->classObj); //-V522 for (SQInteger i=0; i* cd = ClassType::getClassData(vm); HSQOBJECT funcObj; sq_pushobject(vm, cd->classObj); //-V522 sq_pushstring(vm, name, -1); if(SQ_FAILED(sq_get(vm, -2))) { sq_pop(vm, 1); return Function(); } SQObjectType value_type = sq_gettype(vm, -1); if (value_type != OT_CLOSURE && value_type != OT_NATIVECLOSURE && value_type != OT_CLASS) { sq_pop(vm, 2); return Function(); } SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm, -1, &funcObj))); Function ret(vm, cd->classObj, funcObj); sq_pop(vm, 2); return ret; } protected: template void BindFunc(const char* name, Func func, SQFUNCTION func_thunk, SQInteger nparamscheck, bool staticVar = false, const char *docstring=nullptr) { sq_pushobject(vm, GetObject()); sq_pushstring(vm, name, -1); SQUserPointer funcPtr = sq_newuserdata(vm, sizeof(func)); new (funcPtr) Func(func); sq_setreleasehook(vm, -1, ImplaceFreeReleaseHook); sq_newclosure(vm, func_thunk, 1); if (nparamscheck > 0) SQRAT_VERIFY(SQ_SUCCEEDED(sq_setparamscheck(vm, nparamscheck, nullptr))); SQRAT_VERIFY(SQ_SUCCEEDED(sq_setnativeclosurename(vm, -1, name))); if (docstring) SQRAT_VERIFY(SQ_SUCCEEDED(sq_setnativeclosuredocstring(vm, -1, docstring))); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, staticVar))); sq_pop(vm,1); } static SQInteger ClassWeakref(HSQUIRRELVM vm) { sq_weakref(vm, -1); return 1; } static SQInteger ClassTypeof(HSQUIRRELVM vm) { sq_pushstring(vm, ClassType::ClassName().c_str(), -1); return 1; } static SQInteger ClassCloned(HSQUIRRELVM vm) { Sqrat::Var other(vm, 2); return ClassType::CopyFunc(vm)(vm, 1, other.value); } // Initialize the required data structure for the class void InitClass(ClassData* cd) { // push the class sq_pushobject(vm, cd->classObj); // set the typetag to static TypeTag (VM-independent type identity) SQRAT_VERIFY(SQ_SUCCEEDED(sq_settypetag(vm, -1, TypeTag::get()))); // add the default constructor this->BindConstructor(&A::New, 0); // add the set table (static) HSQOBJECT& setTable = cd->setTable; sq_pushstring(vm, "__setTable", -1); sq_newtable(vm); SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm, -1, &setTable))); sq_addref(vm, &setTable); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, true))); // add the get table (static) HSQOBJECT& getTable = cd->getTable; sq_pushstring(vm, "__getTable", -1); sq_newtable(vm); SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm, -1, &getTable))); sq_addref(vm, &getTable); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, true))); // override _set sq_pushstring(vm, "_set", -1); sq_pushobject(vm, setTable); // Push the set table as a free variable sq_newclosure(vm, &sqVarSet, 1); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, false))); // override _get sq_pushstring(vm, "_get", -1); sq_pushobject(vm, getTable); // Push the get table as a free variable sq_newclosure(vm, &sqVarGet, 1); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, false))); // add weakref sq_pushstring(vm, "weakref", -1); sq_newclosure(vm, &Class::ClassWeakref, 0); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, false))); // add _typeof sq_pushstring(vm, "_typeof", -1); sq_newclosure(vm, &Class::ClassTypeof, 0); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, false))); // add _cloned sq_pushstring(vm, "_cloned", -1); sq_newclosure(vm, &Class::ClassCloned, 0); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, false))); // add _tostring sq_pushstring(vm, "_tostring", -1); sq_newclosure(vm, InstanceToString::Format, 0); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, false))); // pop the class sq_pop(vm, 1); } inline void BindAccessor(const char* name, void* var, size_t varSize, SQFUNCTION func, HSQOBJECT table) { // Push the get or set table sq_pushobject(vm, table); sq_pushstring(vm, name, -1); // Push the variable offset as a free variable SQUserPointer varPtr = sq_newuserdata(vm, static_cast(varSize)); memcpy(varPtr, var, varSize); // Create the accessor function sq_newclosure(vm, func, 1); // Add the accessor to the table SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, false))); // Pop get/set table sq_pop(vm, 1); } Class& BindConstructor(SQFUNCTION method, SQInteger nParams) { sq_pushobject(vm, ClassType::getClassData(vm)->classObj); //-V522 sq_pushstring(vm, "constructor", 11); sq_newclosure(vm, method, 0); sq_setparamscheck(vm, nParams+1, NULL); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, false))); sq_pop(vm, 1); return *this; } public: /// Enable inline userdata for this class. Instances will embed the C++ object /// directly in SQInstance memory, avoiding a heap allocation. Only for small types. /// Must be called before any instances are created (before the class is locked). Class& UseInlineUserdata() { static_assert(sizeof(C) <= 256 && alignof(C) <= SQ_ALIGNMENT, "Type is too large or has non-standard alignment for inline userdata"); sq_pushobject(vm, ClassType::getClassData(vm)->classObj); //-V522 sq_setclassudsize(vm, -1, sizeof(C)); sq_pop(vm, 1); return *this; } Class& Ctor() { return BindConstructor(A::iNew, 0); } template Class& Ctor() { return BindConstructor(A::template iNew, sizeof...(Arg)); } Class& SquirrelCtor(SQFUNCTION func, SQInteger nparamscheck=0, const char *typemask=nullptr, const char *docstring=nullptr) { sq_pushobject(vm, ClassType::getClassData(vm)->classObj); //-V522 sq_pushstring(vm, "constructor", 11); sq_newclosure(vm, func, 0); sq_setparamscheck(vm, nparamscheck, typemask); if (docstring) SQRAT_VERIFY(SQ_SUCCEEDED(sq_setnativeclosuredocstring(vm, -1, docstring))); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, false))); sq_pop(vm, 1); return *this; } }; /// Facilitates exposing a C++ class with a base class to Squirrel /// /// \tparam C Class type to expose /// \tparam B Base class type (must already be bound) /// \tparam A An allocator to use when instantiating and destroying class instances of this type in Squirrel /// /// \remarks /// Classes in Squirrel are single-inheritance only, and as such Sqrat only allows for single inheritance as well. /// You MUST bind the base class fully before constructing a derived class. template > class DerivedClass : public Class { using Class::vm; public: DerivedClass(HSQUIRRELVM v, string && className) : Class(v, string(), false) { if (!ClassType::hasClassData(v)) { // Initialize TypeTag inheritance chain (once per type) TypeTag* tag = TypeTag::get(); tag->base = TypeTag::get(); tag->castToBase = [](SQUserPointer p) -> SQUserPointer { return static_cast(static_cast(p)); }; TypeTag::get()->hasDerived = true; TypeTagBase::allTypeTags().insert(tag); // Create per-VM ClassData ClassData* cd = new ClassData; cd->copyFunc = &A::Copy; cd->className = className; if (ClassData::staticClassName.empty()) { ClassData::staticClassName = className; tag->className = ClassData::staticClassName.c_str(); } ClassData* bd = ClassType::getClassData(v); ClassType::setClassData(v, cd); SqratCleanup::getOrCreate(v)->add(cd); HSQOBJECT& classObj = cd->classObj; sq_pushobject(v, bd->classObj); //-V522 SQRAT_VERIFY(SQ_SUCCEEDED(sq_newclass(v, true))); SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(v, -1, &classObj))); sq_addref(v, &classObj); // must addref before the pop! sq_pop(v, 1); InitDerivedClass(cd, bd); } } protected: void InitDerivedClass(ClassData* cd, ClassData* bd) { // push the class sq_pushobject(vm, cd->classObj); // set the typetag to static TypeTag (VM-independent type identity) SQRAT_VERIFY(SQ_SUCCEEDED(sq_settypetag(vm, -1, TypeTag::get()))); // add the default constructor this->BindConstructor(&A::New, 0); // clone the base classes set table (static) HSQOBJECT& setTable = cd->setTable; sq_pushobject(vm, bd->setTable); sq_pushstring(vm, "__setTable", -1); SQRAT_VERIFY(SQ_SUCCEEDED(sq_clone(vm, -2))); sq_remove(vm, -3); SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm, -1, &setTable))); sq_addref(vm, &setTable); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, true))); // clone the base classes get table (static) HSQOBJECT& getTable = cd->getTable; sq_pushobject(vm, bd->getTable); sq_pushstring(vm, "__getTable", -1); SQRAT_VERIFY(SQ_SUCCEEDED(sq_clone(vm, -2))); sq_remove(vm, -3); SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm, -1, &getTable))); sq_addref(vm, &getTable); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, true))); // override _set sq_pushstring(vm, "_set", -1); sq_pushobject(vm, setTable); // Push the set table as a free variable sq_newclosure(vm, sqVarSet, 1); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, false))); // override _get sq_pushstring(vm, "_get", -1); sq_pushobject(vm, getTable); // Push the get table as a free variable sq_newclosure(vm, sqVarGet, 1); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, false))); // add weakref sq_pushstring(vm, "weakref", -1); sq_newclosure(vm, &Class::ClassWeakref, 0); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, false))); // add _typeof sq_pushstring(vm, "_typeof", -1); sq_newclosure(vm, &Class::ClassTypeof, 0); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, false))); // add _cloned sq_pushstring(vm, "_cloned", -1); sq_newclosure(vm, &Class::ClassCloned, 0); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, false))); // add _tostring sq_pushstring(vm, "_tostring", -1); sq_newclosure(vm, InstanceToString::Format, 0); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, false))); // pop the class sq_pop(vm, 1); } }; template struct InstanceToString { static SQInteger Format(HSQUIRRELVM vm) { return ClassType::ToString(vm); } }; } #endif ================================================ FILE: sqrat/include/sqrat/sqratClassType.h ================================================ // Sqrat: altered version by Gaijin Games KFT // SqratClassType: Type Translators // // // Copyright (c) 2009 Brandon Jones // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // #pragma once #if !defined(_SQRAT_CLASSTYPE_H_) #define _SQRAT_CLASSTYPE_H_ #include #include "sqratUtil.h" namespace Sqrat { // The copy function for a class typedef SQInteger (*COPYFUNC)(HSQUIRRELVM, SQInteger, const void*); // VM-independent type identity. Static storage, one per C++ type, lives forever. // Used as SQClass::_typetag. Carries the inheritance chain and cast function. struct TypeTagBase { TypeTagBase* base = nullptr; SQUserPointer (*castToBase)(SQUserPointer) = nullptr; const char* className = nullptr; // set once during first registration bool hasDerived = false; // Validates that a type tag pointer is a Sqrat TypeTag. // Entries are added at registration and never removed (safe to read after init). static hash_set& allTypeTags() { static hash_set s; //-V1096 return s; } static bool isSqratTypeTag(const void* tag) { return tag && allTypeTags().count( const_cast(static_cast(tag))); } }; template struct TypeTag : TypeTagBase { static TypeTag instance; static TypeTag* get() { return &instance; } }; template TypeTag TypeTag::instance; // Per-VM, per-type class data. Stored as userpointer in the VM registry. // Only accessed on cold paths (Push, class registration, instance tracking). template using InstancesMap = class_hash_map; template struct ClassData { HSQOBJECT classObj; HSQOBJECT getTable; HSQOBJECT setTable; COPYFUNC copyFunc = nullptr; string className; InstancesMap* instances = nullptr; // Static class name, set once during first registration. Same for all VMs. static string staticClassName; ClassData() { sq_resetobject(&classObj); sq_resetobject(&getTable); sq_resetobject(&setTable); } ~ClassData() { delete instances; } }; template string ClassData::staticClassName; // Cleanup sentinel -- one per VM, deletes all ClassData entries on VM close. // Stored as a userdata in the registry so its release hook fires during sq_close. struct SqratCleanup { struct Entry { void* ptr; void (*deleter)(void*); }; SQRAT_STD::vector entries; static SQUserPointer key() { static int k = 0; //-V1096 return &k; } template void add(ClassData* cd) { entries.push_back({cd, [](void* p) { delete static_cast*>(p); }}); } static SQInteger release(HSQUIRRELVM, SQUserPointer ptr, SQInteger) { SqratCleanup** pSelf = reinterpret_cast(ptr); SqratCleanup* self = *pSelf; for (auto& e : self->entries) e.deleter(e.ptr); delete self; return 0; } static SqratCleanup* getOrCreate(HSQUIRRELVM vm) { // Look up existing sentinel in registry HSQOBJECT registry; sq_getregistrytableobj(vm, ®istry); HSQOBJECT sentinelKey; sq_resetobject(&sentinelKey); sentinelKey._type = OT_USERPOINTER; sentinelKey._unVal.pUserPointer = key(); HSQOBJECT result; if (SQ_SUCCEEDED(sq_obj_get(vm, ®istry, &sentinelKey, &result, true))) { SqratCleanup** pp; sq_obj_getuserdata(&result, (SQUserPointer*)&pp, nullptr); SqratCleanup *ret = *pp; sq_poptop(vm); return ret; } // Create new sentinel sq_pushregistrytable(vm); sq_pushuserpointer(vm, key()); SqratCleanup** pp = reinterpret_cast(sq_newuserdata(vm, sizeof(SqratCleanup*))); *pp = new SqratCleanup(); sq_setreleasehook(vm, -1, &SqratCleanup::release); sq_rawset(vm, -3); sq_pop(vm, 1); return *pp; } }; // Iterative cast walk using TypeTagBase inheritance chain inline SQUserPointer CastToTarget(SQUserPointer ptr, TypeTagBase* actual, TypeTagBase* target) { while (actual != target) { if (!actual || !actual->castToBase) return nullptr; ptr = actual->castToBase(ptr); actual = actual->base; } return ptr; } // Internal helper class for managing classes template class ClassType { public: // Registry lookup for per-VM ClassData. Uses TypeTag address as key. // Only needed on cold paths (Push, class registration, tracking). static inline ClassData* getClassData(HSQUIRRELVM vm) { HSQOBJECT registry; sq_getregistrytableobj(vm, ®istry); HSQOBJECT key; sq_resetobject(&key); key._type = OT_USERPOINTER; key._unVal.pUserPointer = TypeTag::get(); HSQOBJECT result; if (SQ_FAILED(sq_obj_get(vm, ®istry, &key, &result, true))) return nullptr; ClassData *ret = static_cast*>(result._unVal.pUserPointer); sq_poptop(vm); return ret; } static inline bool hasClassData(HSQUIRRELVM vm) { return getClassData(vm) != nullptr; } static inline const string& ClassName() { return ClassData::staticClassName; } static inline const string& ClassName(HSQUIRRELVM vm) { ClassData* cd = getClassData(vm); SQRAT_ASSERT(cd); return cd->className; //-V522 } static inline COPYFUNC CopyFunc(HSQUIRRELVM vm) { ClassData* cd = getClassData(vm); SQRAT_ASSERT(cd); return cd->copyFunc; //-V522 } // Type checks using static TypeTag -- zero registry lookups static bool IsObjectOfClass(const HSQOBJECT *obj) { TypeTagBase* actualTag = nullptr; if (SQ_FAILED(sq_getobjtypetag(obj, (SQUserPointer*)&actualTag))) return false; if (!TypeTagBase::isSqratTypeTag(actualTag)) return false; for (TypeTagBase* t = actualTag; t; t = t->base) if (t == TypeTag::get()) return true; return false; } // Release hooks. // During sq_close's GC chain walk, vm can be nullptr (_root_vm is already // nulled) and ClassData is already deleted, so map erase is skipped. // Owned: Sqrat created this object, delete it on GC. static SQInteger ReleaseOwned(HSQUIRRELVM vm, SQUserPointer ptr, SQInteger) { if (vm) { ClassData* cd = getClassData(vm); if (cd && cd->instances) cd->instances->erase(static_cast(ptr)); } delete static_cast(ptr); return 0; } // Borrowed: C++ owns this object, just remove from map on GC. static SQInteger ReleaseBorrowed(HSQUIRRELVM vm, SQUserPointer ptr, SQInteger) { if (vm) { ClassData* cd = getClassData(vm); if (cd && cd->instances) cd->instances->erase(static_cast(ptr)); } return 0; } // Inline: object lives inside SQInstance memory, just destruct. static SQInteger DestructInline(HSQUIRRELVM, SQUserPointer ptr, SQInteger) { static_cast(ptr)->~C(); return 0; } static bool PushNativeInstance(HSQUIRRELVM vm, C* ptr) { if (!ptr) { sq_pushnull(vm); return true; } ClassData* cd = getClassData(vm); SQRAT_ASSERT(cd); // class must be registered for this VM if (!cd) return false; // Dedup: if this pointer is already known (owned or borrowed), // return the existing Quirrel instance. Prevents creating a // borrowed duplicate of an owned instance (which would dangle // after GC deletes the owned original). if (!cd->instances) cd->instances = new InstancesMap(); auto it = cd->instances->find(ptr); if (it != cd->instances->end()) { sq_pushobject(vm, it->second); return true; } sq_pushobject(vm, cd->classObj); if (SQ_FAILED(sq_createinstance(vm, -1))) { SQRAT_ASSERT(!"Failed to create class instance"); sq_pop(vm, 1); return false; } sq_remove(vm, -2); SQRAT_VERIFY(SQ_SUCCEEDED(sq_setinstanceup(vm, -1, ptr))); sq_setreleasehook(vm, -1, &ReleaseBorrowed); SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm, -1, &(*cd->instances)[ptr]))); return true; } static bool PushInstanceCopy(HSQUIRRELVM vm, const C& value) { ClassData* cd = getClassData(vm); SQRAT_ASSERT(cd); // class must be registered for this VM if (!cd) return false; sq_pushobject(vm, cd->classObj); if (SQ_FAILED(sq_createinstance(vm, -1))) { SQRAT_ASSERT(!"Failed to create class instance"); sq_pop(vm, 1); return false; } sq_remove(vm, -2); SQRESULT result = cd->copyFunc(vm, -1, &value); SQRAT_UNUSED(result); SQRAT_ASSERT(SQ_SUCCEEDED(result)); return true; } // Hot path: zero registry lookups. Uses static TypeTag for type checking. static C* GetInstance(HSQUIRRELVM vm, SQInteger idx, bool nullAllowed = false) { if (nullAllowed && sq_gettype(vm, idx) == OT_NULL) return nullptr; C* ptr = nullptr; // TypeTag::get() is a static address -- no registry lookup needed if (SQ_FAILED(sq_getinstanceup(vm, idx, (SQUserPointer*)&ptr, TypeTag::get()))) { SQRAT_ASSERTF(0, FormatTypeError(vm, idx, ClassName().c_str()).c_str()); return nullptr; } if (!ptr) { SQRAT_ASSERTF(0, "got unconstructed native class (call base.constructor in the constructor of Squirrel classes that extend native classes)"); return nullptr; } // Cast if actual type differs from requested type (inheritance). // Skip entirely for leaf types (no derived classes) -- the common case. if (TypeTag::get()->hasDerived) { TypeTagBase* actualTag = nullptr; sq_gettypetag(vm, idx, (SQUserPointer*)&actualTag); if (actualTag != TypeTag::get()) { ptr = static_cast(CastToTarget(ptr, actualTag, TypeTag::get())); SQRAT_ASSERT(ptr); // TypeTag chain must be consistent with SQClass chain } } return ptr; } // Stackless instance extraction from HSQOBJECT. // Returns nullptr on null/failure (no asserts -- callers report errors). static C* GetInstanceFromObj(const HSQOBJECT &o) { if (o._type == OT_NULL) return nullptr; SQUserPointer ptr = nullptr; if (SQ_FAILED(sq_obj_getinstanceup(&o, &ptr, TypeTag::get()))) return nullptr; if (!ptr) return nullptr; // unconstructed instance if (TypeTag::get()->hasDerived) { TypeTagBase *actualTag = nullptr; sq_getobjtypetag(&o, (SQUserPointer *)&actualTag); if (actualTag != TypeTag::get()) { ptr = CastToTarget(ptr, actualTag, TypeTag::get()); SQRAT_ASSERT(ptr); } } return static_cast(ptr); } static void SetManagedInstance(HSQUIRRELVM vm, SQInteger idx, C* ptr) { sq_setinstanceup(vm, idx, ptr); ClassData* cd = getClassData(vm); SQRAT_ASSERT(cd); if (!cd->instances) //-V522 cd->instances = new InstancesMap(); sq_setreleasehook(vm, idx, &ReleaseOwned); SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm, idx, &(*cd->instances)[ptr]))); //-V522 } // For SquirrelCtors: allocate instance data, preferring inline if available. // Returns a default-constructed C*. Caller overwrites contents as needed. // Inline path: DestructInline hook, no map. Heap path: ReleaseOwned hook, map. static C* AllocInstanceData(HSQUIRRELVM vm, SQInteger idx) { if constexpr (sizeof(C) <= 256 && alignof(C) <= SQ_ALIGNMENT) { C* ptr = nullptr; sq_getinstanceup(vm, idx, (SQUserPointer*)&ptr, nullptr); if (ptr) { new (ptr) C(); sq_setreleasehook(vm, idx, &DestructInline); return ptr; } } C* ptr = new C; SetManagedInstance(vm, idx, ptr); return ptr; } static bool IsClassInstance(const HSQOBJECT &ho) { SQObjectType type = sq_type(ho); if (type != OT_INSTANCE) return false; TypeTagBase* actualTag = nullptr; if (SQ_FAILED(sq_getobjtypetag(&ho, (SQUserPointer*)&actualTag))) return false; if (!TypeTagBase::isSqratTypeTag(actualTag)) return false; for (TypeTagBase* t = actualTag; t; t = t->base) if (t == TypeTag::get()) return true; return false; } static bool IsClassInstance(HSQUIRRELVM vm, SQInteger idx) { HSQOBJECT ho; sq_getstackobj(vm, idx, &ho); return IsClassInstance(ho); } static SQInteger ToString(HSQUIRRELVM vm) { HSQOBJECT ho; sq_getstackobj(vm, 1, &ho); char buf[256]; int l = snprintf(buf, sizeof(buf), "%s (%p)", ClassName().c_str(), ho._unVal.pInstance); if (l >= (int)sizeof(buf)) l = (int)sizeof(buf) - 1; sq_pushstring(vm, buf, l); return 1; } // Store ClassData in registry as userpointer, keyed by TypeTag address static void setClassData(HSQUIRRELVM vm, ClassData* cd) { sq_pushregistrytable(vm); sq_pushuserpointer(vm, TypeTag::get()); sq_pushuserpointer(vm, cd); sq_rawset(vm, -3); sq_pop(vm, 1); } }; } #endif ================================================ FILE: sqrat/include/sqrat/sqratConst.h ================================================ // Sqrat: altered version by Gaijin Games KFT // SqratConst: Constant and Enumeration Binding // // // Copyright (c) 2009 Brandon Jones // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // #pragma once #if !defined(_SQRAT_CONST_H_) #define _SQRAT_CONST_H_ #include #include #include "sqratObject.h" namespace Sqrat { class Enumeration : public Object { public: Enumeration(HSQUIRRELVM v, bool createTable = true) : Object(v) { if(createTable) { sq_newtable(vm); SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm,-1,&obj))); sq_addref(vm, &obj); sq_pop(vm,1); } } template || SQRAT_STD::is_enum_v, int> = 0> Enumeration& Const(const char* name, T val) { BindValue(name, strlen(name), val, false); return *this; } Enumeration& Const(const char* name, const char* val) { BindValue(name, strlen(name), val, false); return *this; } }; class ConstTable : public Enumeration { public: ConstTable(HSQUIRRELVM v) : Enumeration(v, false) { sq_pushconsttable(vm); SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm,-1, &obj))); sq_addref(v, &obj); sq_pop(v, 1); } template ConstTable& Const(const char* name, T val) { Enumeration::Const(name, val); return *this; } ConstTable& Enum(const char* name, Enumeration& en) { sq_pushobject(vm, GetObject()); sq_pushstring(vm, name, -1); sq_pushobject(vm, en.GetObject()); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, false))); sq_pop(vm,1); // pop table return *this; } }; } #endif ================================================ FILE: sqrat/include/sqrat/sqratFunction.h ================================================ // Sqrat: altered version by Gaijin Games KFT // sqratFunction: Quirrel Function Wrapper // // // Copyright (c) 2009 Brandon Jones // Copyirght 2011 Li-Cheng (Andy) Tai // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // #pragma once #if !defined(_SQRAT_SQFUNC_H_) #define _SQRAT_SQFUNC_H_ #include #include "sqratObject.h" #include "sqratUtil.h" namespace Sqrat { class Function { friend class TableBase; friend class Table; friend class ArrayBase; friend struct Var; private: HSQUIRRELVM vm; HSQOBJECT env, obj; public: Function() { vm = NULL; sq_resetobject(&env); sq_resetobject(&obj); } Function(const Function& sf) : vm(sf.vm), env(sf.env), obj(sf.obj) { sq_addref(vm, &env); sq_addref(vm, &obj); } Function(Function&& sf) : vm(sf.vm), env(sf.env), obj(sf.obj) { sf.vm = nullptr; // don't try release in moved from state } Function(const Function&&)=delete; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Constructs a Function from a slot in an Object /// /// \param e Object that potentially contains a Squirrel function in a slot /// \param slot Name of the slot to look for the Squirrel function in /// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Function(const Object& e, const char* slot) : vm(e.GetVM()), env(e.GetObject()) { sq_addref(vm, &env); Object so = e.GetSlot(slot); obj = so.GetObject(); sq_addref(vm, &obj); SQObjectType value_type = so.GetType(); if (value_type != OT_CLOSURE && value_type != OT_NATIVECLOSURE && value_type != OT_CLASS && value_type != OT_NULL) { // Note that classes can also be considered functions in Squirrel SQRAT_ASSERTF(0, "function not found in slot"); } } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Constructs a Function from two Squirrel objects (one is the environment object and the other is the function object) /// /// \param v VM that the function will exist in /// \param e Squirrel object that should represent the environment of the function /// \param o Squirrel object that should already represent a Squirrel function /// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Function(HSQUIRRELVM v, HSQOBJECT e, HSQOBJECT o) : vm(v), env(e), obj(o) { sq_addref(vm, &env); sq_addref(vm, &obj); } ~Function() { Release(); } Function& operator=(const Function& sf) { if (&sf != this) { Release(); vm = sf.vm; env = sf.env; obj = sf.obj; sq_addref(vm, &env); sq_addref(vm, &obj); } return *this; } Function& operator=(Function&& sf) { if (&sf != this) { Release(); vm = sf.vm; env = sf.env; obj = sf.obj; sf.vm = nullptr; // don't try release in moved from state } return *this; } bool IsNull() const { return sq_isnull(obj); } HSQOBJECT GetEnv() const { return env; } HSQOBJECT& GetEnv() { return env; } HSQOBJECT GetFunc() const { return obj; } HSQOBJECT& GetFunc() { return obj; } HSQUIRRELVM GetVM() const { return vm; } HSQUIRRELVM& GetVM() { return vm; } void Release() { if (vm) { sq_release(vm, &env); sq_release(vm, &obj); sq_resetobject(&env); sq_resetobject(&obj); vm = nullptr; } } bool ExecuteDynArgs(HSQOBJECT const* args, size_t args_count) const { SQRAT_ASSERT(vm); if (!vm) return false; SQInteger top = sq_gettop(vm); sq_pushobject(vm, obj); sq_pushobject(vm, env); for (size_t i = 0; i < args_count; ++i) sq_pushobject(vm, args[i]); HSQUIRRELVM savedVm = vm; // vm can be nulled in sq_call() SQRESULT result = sq_call(vm, args_count + 1, false, SQTrue); if (SQ_FAILED(result)) ReportCallError(); sq_settop(savedVm, top); return SQ_SUCCEEDED(result); } template Sqrat::optional EvalDynArgs(HSQOBJECT const* args, size_t args_count) const { if (!vm) return Sqrat::nullopt; SQInteger top = sq_gettop(vm); sq_pushobject(vm, obj); sq_pushobject(vm, env); for (size_t i = 0; i < args_count; ++i) sq_pushobject(vm, args[i]); HSQUIRRELVM savedVm = vm; SQRESULT result = sq_call(vm, args_count + 1, true, SQTrue); if (SQ_FAILED(result)) { ReportCallError(); sq_settop(savedVm, top); return Sqrat::nullopt; } auto var = Var(savedVm, -1); sq_settop(savedVm, top); return Sqrat::optional{SQRAT_STD::move(var.value)}; } template Sqrat::optional Eval(Args const&... args) const { if (!vm) return Sqrat::nullopt; SQInteger top = sq_gettop(vm); sq_pushobject(vm, obj); sq_pushobject(vm, env); PushArgs(args...); HSQUIRRELVM savedVm = vm; SQRESULT result = sq_call(vm, sizeof...(Args) + 1, true, SQTrue); if (SQ_FAILED(result)) { ReportCallError(); sq_settop(savedVm, top); return Sqrat::nullopt; } auto var = Var(savedVm, -1); sq_settop(savedVm, top); return Sqrat::optional{SQRAT_STD::move(var.value)}; } template bool Execute(Args const&... args) const { SQRAT_ASSERT(vm); if (!vm) return false; static constexpr size_t nArgs = sizeof...(Args); SQInteger top = sq_gettop(vm); sq_pushobject(vm, obj); sq_pushobject(vm, env); PushArgs(args...); HSQUIRRELVM savedVm = vm; // vm can be nulled in sq_call() SQRESULT result = sq_call(vm, nArgs + 1, false, SQTrue); if (SQ_FAILED(result)) ReportCallError(); sq_settop(savedVm, top); return SQ_SUCCEEDED(result); } template bool operator()(Args const&... args) const { return Execute(args...); } bool IsEqual(const Sqrat::Function &so) const { if (IsNull() && so.IsNull()) // Nulls may have uninitialized vm return true; if (GetVM() != so.GetVM()) return false; return sq_obj_is_equal(vm, &obj, &so.obj); } private: template void PushArgs(Arg const& arg, Tail const&... tail) const { PushVar(vm, arg); PushArgs(tail...); } void PushArgs() const { } template void PushArgsWithoutRet(Arg const& arg, Tail const&... tail) const { PushVar(vm, arg); PushArgsWithoutRet(tail...); } template void PushArgsWithoutRet(R const&) const { } void ReportCallError() const { if (!vm) return; SQPRINTFUNCTION errpf = sq_geterrorfunc(vm); if (!errpf) return; sq_pushobject(vm, obj); if (SQ_SUCCEEDED(sq_getclosurename(vm, -1))) { const char *name; if (SQ_SUCCEEDED(sq_getstring(vm, -1, &name))) errpf(vm, "Failed to call squirrel function %s\n", name); sq_pop(vm, 1); } sq_pop(vm, 1); } }; /// Used to get and push Function instances to and from the stack as references (functions are always references in Squirrel) template<> struct Var { Function value; ///< The actual value of get operations ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Attempts to get the value off the stack at idx as a Function /// /// \param vm Target VM /// \param idx Index trying to be read /// /// \remarks /// Initializes environment to null /// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// Var(HSQUIRRELVM vm, SQInteger idx) { HSQOBJECT sqEnv; HSQOBJECT sqValue; sq_resetobject(&sqEnv); SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm, idx, &sqValue))); value = Function(vm, sqEnv, sqValue); SQObjectType value_type = sq_gettype(vm, idx); if (value_type != OT_CLOSURE && value_type != OT_NATIVECLOSURE && value_type != OT_CLASS && value_type != OT_NULL) { SQRAT_ASSERTF(0, FormatTypeError(vm, idx, "closure").c_str()); } } /// \remarks /// Initializes environment to given object Var(HSQUIRRELVM vm, SQInteger idx, HSQOBJECT env) { HSQOBJECT sqValue; SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm, idx, &sqValue))); value = Function(vm, env, sqValue); SQObjectType value_type = sq_gettype(vm, idx); if (value_type != OT_CLOSURE && value_type != OT_NATIVECLOSURE && value_type != OT_CLASS && value_type != OT_NULL) { SQRAT_ASSERTF(0, FormatTypeError(vm, idx, "closure").c_str()); } } /// Called by Sqrat::PushVar to put a Function on the stack static void push(HSQUIRRELVM vm, const Function& value) { sq_pushobject(vm, value.GetFunc()); } static const char * getVarTypeName() { return "closure"; } static bool check_type(HSQUIRRELVM vm, SQInteger idx) { SQObjectType type = sq_gettype(vm, idx); return type == OT_CLOSURE || type == OT_NATIVECLOSURE || type == OT_NULL || type == OT_CLASS; } }; template<> struct Var : Var {Var(HSQUIRRELVM vm, SQInteger idx) : Var(vm, idx) {}}; template<> struct Var : Var {Var(HSQUIRRELVM vm, SQInteger idx) : Var(vm, idx) {}}; } #endif ================================================ FILE: sqrat/include/sqrat/sqratGlobalMethods.h ================================================ // Sqrat: altered version by Gaijin Games KFT // SqratGlobalMethods: Global Methods // // // Copyright (c) 2009 Brandon Jones // Copyirght 2011 Li-Cheng (Andy) Tai // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // #pragma once #if !defined(_SQRAT_GLOBAL_METHODS_H_) #define _SQRAT_GLOBAL_METHODS_H_ #include #include "sqratTypes.h" namespace Sqrat { template> class SqThunkGen; template class SqThunkGen { public: template static SQInteger Func(HSQUIRRELVM vm) { if (!vargs::check_var_types(vm, startIdx)) return SQ_ERROR; Callable *method; sq_getuserdata(vm, -1, (SQUserPointer *)&method, NULL); auto vars = vargs::make_vars(vm, startIdx); vargs::apply(*method, vars); return 0; } }; template class SqThunkGen { public: template static SQInteger Func(HSQUIRRELVM vm) { if (!vargs::check_var_types(vm, startIdx)) return SQ_ERROR; Callable *method; sq_getuserdata(vm, -1, (SQUserPointer *)&method, NULL); auto vars = vargs::make_vars(vm, startIdx); PushVar(vm, vargs::apply(*method, vars)); return 1; } }; template SQFUNCTION SqGlobalThunk() { return &SqThunkGen::template Func<2>; } template SQFUNCTION SqMemberGlobalThunk() { return &SqThunkGen::template Func<1>; } } #endif ================================================ FILE: sqrat/include/sqrat/sqratMemberMethods.h ================================================ // Sqrat: altered version by Gaijin Games KFT // SqratMemberMethods: Member Methods // // // Copyright (c) 2009 Brandon Jones // Copyright 2011 Li-Cheng (Andy) Tai // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // #pragma once #if !defined(_SQRAT_MEMBER_METHODS_H_) #define _SQRAT_MEMBER_METHODS_H_ #include #include "sqratTypes.h" namespace Sqrat { template> struct SqMemberThunkGen; template struct SqMemberThunkGen { static SQInteger Func(HSQUIRRELVM vm) { SQRAT_ASSERT(sq_gettop(vm) == 2 + sizeof...(A)); // CallNative validates; extra guard if (!vargs::check_var_types(vm, 2)) return SQ_ERROR; MemberFunc *methodPtr; sq_getuserdata(vm, -1, (SQUserPointer *)&methodPtr, NULL); HSQOBJECT selfObj; sq_getstackobj(vm, 1, &selfObj); C *ptr = ClassType::GetInstanceFromObj(selfObj); if (!ptr) return sq_throwerror(vm, FormatTypeError(vm, 1, ClassType::ClassName().c_str()).c_str()); auto vars = vargs::make_vars(vm, 2); R ret = vargs::apply_member(ptr, *methodPtr, vars); PushVar(vm, ret); return 1; } }; template struct SqMemberThunkGen { static SQInteger Func(HSQUIRRELVM vm) { SQRAT_ASSERT(sq_gettop(vm) == 2 + sizeof...(A)); // CallNative validates; extra guard if (!vargs::check_var_types(vm, 2)) return SQ_ERROR; MemberFunc *methodPtr; sq_getuserdata(vm, -1, (SQUserPointer *)&methodPtr, NULL); HSQOBJECT selfObj; sq_getstackobj(vm, 1, &selfObj); C *ptr = ClassType::GetInstanceFromObj(selfObj); if (!ptr) return sq_throwerror(vm, FormatTypeError(vm, 1, ClassType::ClassName().c_str()).c_str()); auto vars = vargs::make_vars(vm, 2); vargs::apply_member(ptr, *methodPtr, vars); return 0; } }; // // Member Function Resolvers // template SQFUNCTION SqMemberFunc() { return &SqMemberThunkGen::Func; } // // Variable Get // template inline SQInteger sqDefaultGet(HSQUIRRELVM vm) { HSQOBJECT selfObj; sq_getstackobj(vm, 1, &selfObj); C* ptr = ClassType::GetInstanceFromObj(selfObj); if (!ptr) return sq_throwerror(vm, FormatTypeError(vm, 1, ClassType::ClassName().c_str()).c_str()); typedef V C::*M; M* memberPtr = NULL; sq_getuserdata(vm, -1, (SQUserPointer*)&memberPtr, NULL); // Get Member... M member = *memberPtr; PushVarR(vm, ptr->*member); return 1; } template inline SQInteger sqStaticGet(HSQUIRRELVM vm) { typedef V *M; M* memberPtr = NULL; sq_getuserdata(vm, -1, (SQUserPointer*)&memberPtr, NULL); // Get Member... M member = *memberPtr; PushVarR(vm, *member); return 1; } inline SQInteger sqVarGet(HSQUIRRELVM vm) { // Find the get method in the get table sq_push(vm, 2); if (SQ_FAILED(sq_rawget(vm, -2))) { sq_pushnull(vm); return sq_throwobject(vm); } // push 'this' sq_push(vm, 1); // Call the getter SQRESULT result = sq_call(vm, 1, true, SQTrue); return SQ_SUCCEEDED(result) ? 1 : SQ_ERROR; } // // Variable Set // template inline SQInteger sqDefaultSet(HSQUIRRELVM vm) { HSQOBJECT selfObj; sq_getstackobj(vm, 1, &selfObj); C* ptr = ClassType::GetInstanceFromObj(selfObj); if (!ptr) return sq_throwerror(vm, FormatTypeError(vm, 1, ClassType::ClassName().c_str()).c_str()); typedef V C::*M; M* memberPtr = NULL; sq_getuserdata(vm, -1, (SQUserPointer*)&memberPtr, NULL); // Get Member... M member = *memberPtr; if constexpr (SQRAT_STD::is_pointer_v || SQRAT_STD::is_reference_v) { ptr->*member = Var(vm, 2).value; } else { ptr->*member = Var(vm, 2).value; } return 0; } template inline SQInteger sqStaticSet(HSQUIRRELVM vm) { typedef V *M; M* memberPtr = NULL; sq_getuserdata(vm, -1, (SQUserPointer*)&memberPtr, NULL); // Get Member... M member = *memberPtr; if constexpr (SQRAT_STD::is_pointer_v || SQRAT_STD::is_reference_v) { *member = Var(vm, 2).value; } else { *member = Var(vm, 2).value; } return 0; } inline SQInteger sqVarSet(HSQUIRRELVM vm) { // Find the set method in the set table sq_push(vm, 2); if (SQ_FAILED(sq_rawget(vm, -2))) { sq_pushnull(vm); return sq_throwobject(vm); } // push 'this' sq_push(vm, 1); sq_push(vm, 3); // Call the setter SQRESULT result = sq_call(vm, 2, false, SQTrue); return SQ_SUCCEEDED(result) ? 0 : SQ_ERROR; } } #endif ================================================ FILE: sqrat/include/sqrat/sqratObject.h ================================================ // Sqrat: altered version by Gaijin Games KFT // SqratObject: Referenced Quirrel Object Wrapper // // // Copyright (c) 2009 Brandon Jones // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // #pragma once #if !defined(_SQRAT_OBJECT_H_) #define _SQRAT_OBJECT_H_ #include #include #include "sqratAllocator.h" #include "sqratTypes.h" #include "sqratUtil.h" namespace Sqrat { class Table; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// The base class for classes that represent Squirrel objects /// /// \remarks /// All Object and derived classes MUST be destroyed before calling sq_close or your application will crash when exiting. /// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// class Object { protected: HSQUIRRELVM vm; HSQOBJECT obj; public: Object(HSQUIRRELVM v) : vm(v){ sq_resetobject(&obj); } Object() : vm(0) { sq_resetobject(&obj); } Object(const Object& so) : vm(so.vm), obj(so.obj) { sq_addref(vm, &obj); } Object(Object && so) : vm(so.vm), obj(so.obj) { sq_resetobject(&so.obj); } Object(const Object &&)=delete; Object(HSQOBJECT o, HSQUIRRELVM v) : vm(v), obj(o) { sq_addref(vm, &obj); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Constructs an Object from a known/bound type /// /// \param t If t is pointer to a C++ class instance then it has to be bound already, otherwise it's ref to value of known type /// \param v VM that the object will exist in /// /// \tparam T Type /// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template , bool> = false> Object(const T &t, HSQUIRRELVM v) : vm(v) { Var::push(vm, t); SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm, -1, &obj))); sq_addref(vm, &obj); sq_poptop(vm); } Object(const char *str, HSQUIRRELVM v, SQInteger str_len = -1) : vm(v) { if (str) { sq_pushstring(vm, str, str_len); SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm, -1, &obj))); sq_addref(vm, &obj); sq_poptop(vm); } else sq_resetobject(&obj); } Object(char *str, HSQUIRRELVM v, SQInteger str_len = -1) : vm(v) { if (str) { sq_pushstring(vm, str, str_len); SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm, -1, &obj))); sq_addref(vm, &obj); sq_poptop(vm); } else sq_resetobject(&obj); } template , bool> = false> Object(T t, HSQUIRRELVM v) : vm(v) { sq_resetobject(&obj); if constexpr (SQRAT_STD::is_same_v) { obj._type = OT_BOOL; obj._unVal.nInteger = t ? 1 : 0; } else if constexpr (SQRAT_STD::is_floating_point_v) { obj._type = OT_FLOAT; obj._unVal.fFloat = (SQFloat)t; } else { obj._type = OT_INTEGER; obj._unVal.nInteger = (SQInteger)t; } } ~Object() { Release(); } Object& operator=(const Object& so) { if (&so != this) { Release(); vm = so.vm; obj = so.obj; sq_addref(vm, &obj); } return *this; } Object& operator=(Object && so) { if (&so != this) { Release(); vm = so.vm; obj = so.obj; sq_resetobject(&so.obj); } return *this; } HSQUIRRELVM& GetVM() { return vm; } HSQUIRRELVM GetVM() const { return vm; } SQObjectType GetType() const { return GetObject()._type; } bool IsNull() const { return sq_isnull(GetObject()); } const HSQOBJECT &GetObject() const { return obj; } HSQOBJECT& GetObject() { return obj; } operator HSQOBJECT&() { return GetObject(); } void Release() { sq_release(vm, &obj); sq_resetobject(&obj); } bool IsEqual(const Sqrat::Object &so) const { if (IsNull() && so.IsNull()) // Nulls may have uninitialized vm return true; if (GetVM() != so.GetVM()) return false; return sq_obj_is_equal(vm, &obj, &so.obj); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Attempts to get the value of a slot from the object /// \return An Object representing the value of the slot (can be a null object if nothing was found) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template Object GetSlotImpl(const char* slot, int slen) const { HSQOBJECT slotObj; sq_pushobject(vm, GetObject()); sq_pushstring(vm, slot, slen); SQRESULT res = raw ? sq_rawget(vm, -2) : sq_get(vm, -2); if(SQ_FAILED(res)) { sq_pop(vm, 1); return Object(vm); // Return a NULL object } else { SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm, -1, &slotObj))); Object ret(slotObj, vm); // must addref before the pop! sq_pop(vm, 2); return ret; } } Object GetSlot(const char* slot) const { return GetSlotImpl(slot, strlen(slot)); } Object RawGetSlot(const char* slot) const { return GetSlotImpl(slot, strlen(slot)); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Attempts to get the value of an index from the object /// \return An Object representing the value of the slot (can be a null object if nothing was found) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template Object GetSlotImpl(SQInteger index) const { HSQOBJECT slotObj; sq_pushobject(vm, GetObject()); sq_pushinteger(vm, index); SQRESULT res = raw ? sq_rawget(vm, -2) : sq_get(vm, -2); if(SQ_FAILED(res)) { sq_pop(vm, 1); return Object(vm); // Return a NULL object } else { SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm, -1, &slotObj))); Object ret(slotObj, vm); // must addref before the pop! sq_pop(vm, 2); return ret; } } Object GetSlot(SQInteger index) const { return GetSlotImpl(index); } Object RawGetSlot(SQInteger index) const { return GetSlotImpl(index); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Attempts to get the value of an key from the object /// \param slot Key (of any type) of the slot /// \return An Object representing the value of the slot (can be a null object if nothing was found) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template Object GetSlotImpl(const Object& slot) const { SQRAT_ASSERT(slot.IsNull() || slot.GetVM() == vm); HSQOBJECT res; if (SQ_FAILED(sq_obj_get(vm, &obj, &slot.obj, &res, raw))) return Object(vm); Object ret(res, vm); sq_poptop(vm); return ret; } Object GetSlot(const Object& slot) const { return GetSlotImpl(slot); } Object RawGetSlot(const Object& slot) const { return GetSlotImpl(slot); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Casts the object to a certain C++ type /// \tparam T Type to cast to /// \return A copy of the value of the Object with the given type ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template T Cast() const { static_assert(VarControlsValueLifeTime::value == 0, "direct cast to T failed due to value is bound to Var. use GetVar() instead"); return GetVar().value; } template Var GetVar() const { sq_pushobject(vm, GetObject()); Var ret(vm, -1); sq_pop(vm, 1); return ret; } /// Gets object slot value as a certain C++ type template T GetSlotValueImpl(const char* slot, int slen, T def_val) const { static_assert(VarControlsValueLifeTime::value == 0, "direct cast to T failed due to value is bound to Var"); sq_pushobject(vm, GetObject()); sq_pushstring(vm, slot, slen); SQRESULT res = raw ? sq_rawget(vm, -2) : sq_get(vm, -2); if (SQ_FAILED(res)) { sq_pop(vm, 1); return def_val; } else if (!Var::check_type(vm, -1)) { sq_pop(vm, 2); return def_val; } else { T ret = Var(vm, -1).value; sq_pop(vm, 2); return ret; } } template T GetSlotValue(const char* slot, T def_val) const { return GetSlotValueImpl(slot, strlen(slot), def_val); } template T RawGetSlotValue(const char* slot, T def_val) const { return GetSlotValueImpl(slot, strlen(slot), def_val); } template T GetSlotValueImpl(SQInteger slot, T def_val) const { static_assert(VarControlsValueLifeTime::value == 0, "direct cast to T failed due to value is bound to Var"); sq_pushobject(vm, GetObject()); sq_pushinteger(vm, slot); SQRESULT res = raw ? sq_rawget(vm, -2) : sq_get(vm, -2); if (SQ_FAILED(res)) { sq_pop(vm, 1); return def_val; } else if (!Var::check_type(vm, -1)) { sq_pop(vm, 2); return def_val; } else { T ret = Var(vm, -1).value; sq_pop(vm, 2); return ret; } } template T GetSlotValue(SQInteger slot, T def_val) const { return GetSlotValueImpl(slot, def_val); } template T RawGetSlotValue(SQInteger slot, T def_val) const { return GetSlotValueImpl(slot, def_val); } /// Gets object slot value as a certain C++ type template T GetSlotValueImpl(const Object &key, T def_val) const { SQRAT_ASSERT(key.IsNull() || key.GetVM() == vm); static_assert(VarControlsValueLifeTime::value == 0, "direct cast to T failed due to value is bound to Var"); HSQOBJECT res; if (SQ_FAILED(sq_obj_get(vm, &obj, &key.obj, &res, raw))) return def_val; // sq_obj_get already pushed result to VM stack if constexpr (has_direct_var_v) { T ret = Var::check_type(res) ? Var(res).value : def_val; sq_pop(vm, 1); return ret; } else { if (!Var::check_type(vm, -1)) { sq_pop(vm, 1); return def_val; } T ret = Var(vm, -1).value; sq_pop(vm, 1); return ret; } } template T GetSlotValue(const Object &key, T def_val) const { return GetSlotValueImpl(key, def_val); } template T RawGetSlotValue(const Object &key, T def_val) const { return GetSlotValueImpl(key, def_val); } template inline Object operator[](T slot) { return GetSlot(slot); } template const Object operator[](T slot) const { return GetSlot(slot); } SQInteger GetSize() const { return sq_obj_getsize(&obj); } struct iterator; bool Next(iterator& iter) const; void FreezeSelf() { obj._flags |= SQOBJ_FLAG_IMMUTABLE; } void UnfreezeSelf() { obj._flags &= ~SQOBJ_FLAG_IMMUTABLE; } const SQRAT_STD::string_view GetString(const SQRAT_STD::string_view def_val = {}) const { const char *str = sq_objtostring(&obj); if (!str) return def_val; return SQRAT_STD::string_view(str, sq_obj_getsize(&obj)); } protected: template void BindFunc(const char* name, Func func, SQFUNCTION func_thunk, SQInteger nparamscheck, bool staticVar = false, const char *docstring=nullptr) { sq_pushobject(vm, GetObject()); sq_pushstring(vm, name, -1); SQUserPointer funcPtr = sq_newuserdata(vm, sizeof(func)); new (funcPtr) Func(func); sq_setreleasehook(vm, -1, ImplaceFreeReleaseHook); sq_newclosure(vm, func_thunk, 1); if (nparamscheck > 0) SQRAT_VERIFY(SQ_SUCCEEDED(sq_setparamscheck(vm, nparamscheck, nullptr))); SQRAT_VERIFY(SQ_SUCCEEDED(sq_setnativeclosurename(vm, -1, name))); if (docstring) SQRAT_VERIFY(SQ_SUCCEEDED(sq_setnativeclosuredocstring(vm, -1, docstring))); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, staticVar))); sq_pop(vm,1); // pop table } template void BindFunc(SQInteger index, Func func, SQFUNCTION func_thunk, SQInteger nparamscheck, bool staticVar = false, const char *docstring=nullptr) { sq_pushobject(vm, GetObject()); sq_pushinteger(vm, index); SQUserPointer funcPtr = sq_newuserdata(vm, sizeof(func)); new (funcPtr) Func(func); sq_setreleasehook(vm, -1, ImplaceFreeReleaseHook); sq_newclosure(vm, func_thunk, 1); if (nparamscheck > 0) SQRAT_VERIFY(SQ_SUCCEEDED(sq_setparamscheck(vm, nparamscheck, nullptr))); if (docstring) SQRAT_VERIFY(SQ_SUCCEEDED(sq_setnativeclosuredocstring(vm, -1, docstring))); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, staticVar))); sq_pop(vm,1); // pop table } /// Set the value of a variable on the object. Changes to values set this way are not reciprocated template #ifdef _MSC_VER __declspec(noinline) // To force `SetValue` to be inlined for `strlen(name)` #elif defined(__GNUC__) __attribute__((noinline)) #endif inline void BindValue(const char* name, int nlen, const V& val, bool staticVar = false) { sq_pushobject(vm, GetObject()); sq_pushstring(vm, name, nlen); PushVar(vm, val); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, staticVar))); sq_pop(vm,1); // pop table } template inline void BindValue(const SQRAT_STD::string_view name, const V& val, bool staticVar = false) { sq_pushobject(vm, GetObject()); sq_pushstring(vm, name.data(), name.size()); PushVar(vm, val); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, staticVar))); sq_pop(vm,1); // pop table } template inline void BindValue(const SQInteger index, const V& val, bool staticVar = false) { sq_pushobject(vm, GetObject()); sq_pushinteger(vm, index); PushVar(vm, val); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, staticVar))); sq_pop(vm,1); // pop table } template inline void BindValue(const Object &key, const V& val, bool staticVar = false) { PushVar(vm, val); HSQOBJECT valObj; sq_getstackobj(vm, -1, &valObj); SQRAT_VERIFY(SQ_SUCCEEDED(sq_obj_newslot(vm, &obj, &key.obj, &valObj, staticVar))); sq_poptop(vm); } // Set the value of an instance on the object. Changes to values set this way are reciprocated back to the source instance template inline void BindInstance(const char* name, V* val, bool staticVar = false) { sq_pushobject(vm, GetObject()); sq_pushstring(vm, name, -1); PushVar(vm, val); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, staticVar))); sq_pop(vm,1); // pop table } template inline void BindInstance(const SQInteger index, V* val, bool staticVar = false) { sq_pushobject(vm, GetObject()); sq_pushinteger(vm, index); PushVar(vm, val); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, staticVar))); sq_pop(vm,1); // pop table } template inline void BindInstance(const Object &key, V* val, bool staticVar = false) { PushVar(vm, val); HSQOBJECT valObj; sq_getstackobj(vm, -1, &valObj); SQRAT_VERIFY(SQ_SUCCEEDED(sq_obj_newslot(vm, &obj, &key.obj, &valObj, staticVar))); sq_poptop(vm); } }; // Optimized no-stack-push-and-pop versions template<> inline int32_t Object::Cast() const { return sq_objtointeger(&obj); } template<> inline int64_t Object::Cast() const { return sq_objtointeger(&obj); } template<> inline float Object::Cast() const { return sq_objtofloat(&obj); } template<> inline double Object::Cast() const { return sq_objtofloat(&obj); } template<> inline bool Object::Cast() const { return sq_obj_is_true(&obj) != SQFalse; } template<> #ifdef _MSC_VER __declspec(noinline) // To force `SetValue` to be inlined for `strlen(name)` #elif defined(__GNUC__) __attribute__((noinline)) #endif inline void Object::BindValue(const char* name, int nlen, const int & val, bool staticVar /* = false */) { sq_pushobject(vm, GetObject()); sq_pushstring(vm, name, nlen); PushVar(vm, val); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, staticVar))); sq_pop(vm,1); // pop table } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Used to get and push Object instances to and from the stack as references (Object is always a reference) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template<> struct Var { Object value; ///< The actual value of get operations /// Attempts to get the value off the stack at idx as an Object Var(HSQUIRRELVM vm, SQInteger idx) { HSQOBJECT sqValue; SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm, idx, &sqValue))); value = Object(sqValue, vm); } /// Called by Sqrat::PushVar to put an Object on the stack static void push(HSQUIRRELVM vm, const Object& value) { sq_pushobject(vm, value.GetObject()); } static const char * getVarTypeName() { return "object"; } static bool check_type(HSQUIRRELVM /*vm*/, SQInteger /*idx*/) { return true; } }; template<> struct Var : Var {Var(HSQUIRRELVM vm, SQInteger idx) : Var(vm, idx) {}}; template<> struct Var : Var {Var(HSQUIRRELVM vm, SQInteger idx) : Var(vm, idx) {}}; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Iterator for going over the slots in the object using Object::Next ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// struct Object::iterator { friend class Object; const char* getName() { HSQOBJECT hKey = key.GetObject(); return sq_objtostring(&hKey); } HSQOBJECT getKey() { return key.GetObject(); } HSQOBJECT getValue() { return value.GetObject(); } private: Object index, key, value; }; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Used to go through all the slots in an Object (same limitations as sq_next) /// \param iter An iterator being used for going through the slots /// \return Whether there is a next slot ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// inline bool Object::Next(iterator& iter) const { sq_pushobject(vm, obj); sq_pushobject(vm, iter.index.GetObject()); if (SQ_SUCCEEDED(sq_next(vm,-2))) { iter.index = Var(vm, -3).value; iter.key = Var(vm, -2).value; iter.value = Var(vm, -1).value; sq_pop(vm, 4); return true; } else { sq_pop(vm, 2); return false; } } } // namespace Sqrat #endif ================================================ FILE: sqrat/include/sqrat/sqratScript.h ================================================ // Sqrat: altered version by Gaijin Games KFT // SqratScript: Script Compilation and Execution // // // Copyright (c) 2009 Brandon Jones // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // #pragma once #if !defined(_SQRAT_SCRIPT_H_) #define _SQRAT_SCRIPT_H_ #include #include #include #include "sqratObject.h" namespace Sqrat { /// Helper class for managing Squirrel scripts class Script : public Object { public: Script(HSQUIRRELVM v) : Object(v) { } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Sets up the Script using a string containing a Squirrel script /// /// \param script String containing a file path to a Squirrel script /// \param errMsg String that is filled with any errors that may occur /// \param name Optional string containing the script's name (for errors) /// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool CompileString(const string_view &script, string &errMsg, const string_view &name = string_view(), const HSQOBJECT *bindings=nullptr) { if(!sq_isnull(obj)) { sq_release(vm, &obj); sq_resetobject(&obj); } if(SQ_FAILED(sq_compile(vm, script.data(), static_cast(script.size() /** sizeof(char)*/), name.data(), true, bindings))) { errMsg = LastErrorString(vm); return false; } sq_getstackobj(vm,-1,&obj); sq_addref(vm, &obj); sq_pop(vm, 1); return true; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Sets up the Script using a file containing a Squirrel script /// /// \param path File path containing a Squirrel script /// \param errMsg String that is filled with any errors that may occur /// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool CompileFile(const string& path, string& errMsg) { if(!sq_isnull(obj)) { sq_release(vm, &obj); sq_resetobject(&obj); } if(SQ_FAILED(sqstd_loadfile(vm, path.c_str(), true))) { errMsg = LastErrorString(vm); return false; } sq_getstackobj(vm,-1,&obj); sq_addref(vm, &obj); sq_pop(vm, 1); return true; } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Runs the script /// /// \param errMsg String that is filled with any errors that may occur /// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool Run(string& errMsg, Object * context = NULL) { if(!sq_isnull(obj)) { SQRESULT result; SQInteger top = sq_gettop(vm); sq_pushobject(vm, obj); if (!context) sq_pushroottable(vm); else sq_pushobject(vm, context->GetObject()); result = sq_call(vm, 1, false, true); sq_settop(vm, top); if(SQ_FAILED(result)) { errMsg = LastErrorString(vm); return false; } return true; } return false; } }; } #endif ================================================ FILE: sqrat/include/sqrat/sqratTable.h ================================================ // Sqrat: altered version by Gaijin Games KFT // SqratTable: Table Binding // // // Copyright (c) 2009 Brandon Jones // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // #pragma once #if !defined(_SQRAT_TABLE_H_) #define _SQRAT_TABLE_H_ #include #include #include "sqratObject.h" #include "sqratFunction.h" #include "sqratGlobalMethods.h" namespace Sqrat { template class Class; enum class FunctionPurity { Pure, SideEffects, }; class TableBase : public Object { public: TableBase() { } TableBase(HSQUIRRELVM v) : Object(v) { } TableBase(const Object& obj) : Object(obj) { } TableBase(Object && obj) : Object(SQRAT_STD::move(obj)) { } TableBase(HSQOBJECT o, HSQUIRRELVM v) : Object(o, v) { } /// Binds a Table or Class to the Table (can be used to facilitate namespaces) void Bind(const char* name, Object& object) { BindImpl(name, object); } template void Bind(const char* name, Class& klass) { BindImpl(name, klass); } /// Binds a raw Squirrel closure to the Table TableBase& SquirrelFunc(const char* name, SQFUNCTION func, SQInteger nparamscheck, const char *typemask=nullptr, const char *docstring=nullptr, SQInteger nfreevars=0, const Object *freevars=nullptr, FunctionPurity purity=FunctionPurity::SideEffects) { sq_pushobject(vm, GetObject()); sq_pushstring(vm, name, -1); for (SQInteger i=0; i TableBase& SetValue(const char* name, const V& val) { //-V1071 BindValue(name, strlen(name), val, false); return *this; } template TableBase& SetValue(const SQRAT_STD::string_view& name, const V& val) { //-V1071 BindValue(name, val, false); return *this; } template TableBase& SetValue(const SQInteger index, const V& val) { BindValue(index, val, false); return *this; } template TableBase& SetValue(const Object &key, const V& val) { BindValue(key, val, false); return *this; } template TableBase& SetInstance(const char* name, V* val) { BindInstance(name, val, false); return *this; } template TableBase& SetInstance(const SQInteger index, V* val) { BindInstance(index, val, false); return *this; } template TableBase& SetInstance(const Object &key, V* val) { BindInstance(key, val, false); return *this; } /// Sets a key in the Table to a specific function template TableBase& Func(const char* name, F method, const char *docstring=nullptr) { //-V1071 BindFunc(name, method, SqGlobalThunk(), 1+SqGetArgCount(), false, docstring); return *this; } template bool HasKeyImpl(const char* name) const { sq_pushobject(vm, obj); sq_pushstring(vm, name, -1); if (SQ_FAILED(raw ? sq_rawget(vm, -2) : sq_get(vm, -2))) { sq_pop(vm, 1); return false; } sq_pop(vm, 2); return true; } bool HasKey(const char* name) const { return HasKeyImpl(name); } bool RawHasKey(const char* name) const { return HasKeyImpl(name); } bool HasKey(const Object &key) const { SQRAT_ASSERT(key.IsNull() || key.GetVM() == vm); const HSQOBJECT &hSelf = GetObject(), &hKey = key.GetObject(); HSQOBJECT out; bool res = SQ_SUCCEEDED(sq_obj_get(vm, &hSelf, &hKey, &out, /*raw*/ false)); if (res) sq_poptop(vm); return res; } bool RawHasKey(const Object &key) const { SQRAT_ASSERT(key.IsNull() || key.GetVM() == vm); const HSQOBJECT &hSelf = GetObject(), &hKey = key.GetObject(); HSQOBJECT out; bool res = SQ_SUCCEEDED(sq_obj_get(vm, &hSelf, &hKey, &out, /*raw*/ true)); if (res) sq_poptop(vm); return res; } template Function GetFunctionImpl(const char* name) const { HSQOBJECT funcObj; sq_pushobject(vm, GetObject()); sq_pushstring(vm, name, -1); if(SQ_FAILED(raw ? sq_rawget(vm, -2) : sq_get(vm, -2))) { sq_pop(vm, 1); return Function(); } SQObjectType value_type = sq_gettype(vm, -1); if (value_type != OT_CLOSURE && value_type != OT_NATIVECLOSURE) { sq_pop(vm, 2); return Function(); } SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm, -1, &funcObj))); Function ret(vm, GetObject(), funcObj); // must addref before the pop! sq_pop(vm, 2); return ret; } Function GetFunction(const char* name) const { return GetFunctionImpl(name); } Function RawGetFunction(const char* name) const { return GetFunctionImpl(name); } template Function GetFunctionImpl(const SQInteger index) const { HSQOBJECT funcObj; sq_pushobject(vm, GetObject()); sq_pushinteger(vm, index); if(SQ_FAILED(raw ? sq_rawget(vm, -2) : sq_get(vm, -2))) { sq_pop(vm, 1); return Function(); } SQObjectType value_type = sq_gettype(vm, -1); if (value_type != OT_CLOSURE && value_type != OT_NATIVECLOSURE) { sq_pop(vm, 2); return Function(); } SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm, -1, &funcObj))); Function ret(vm, GetObject(), funcObj); // must addref before the pop! sq_pop(vm, 2); return ret; } template Function GetFunctionImpl(const Object& key) const { SQRAT_ASSERT(key.IsNull() || key.GetVM() == vm); const HSQOBJECT &hSelf = GetObject(), &hKey = key.GetObject(); HSQOBJECT funcObj; if (SQ_FAILED(sq_obj_get(vm, &hSelf, &hKey, &funcObj, raw))) return Function(); if (funcObj._type != OT_CLOSURE && funcObj._type != OT_NATIVECLOSURE) { sq_poptop(vm); return Function(); } Function ret(vm, GetObject(), funcObj); sq_poptop(vm); return ret; } Function GetFunction(const Object& key) const { return GetFunctionImpl(key); } Function RawGetFunction(const Object& key) const { return GetFunctionImpl(key); } template bool DeleteSlotImpl(const char* name) const { sq_pushobject(vm, obj); sq_pushstring(vm, name, -1); if (SQ_FAILED(raw ? sq_rawdeleteslot(vm, -2, false) : sq_deleteslot(vm, -2, false))) { sq_pop(vm, 1); return false; } sq_pop(vm, 1); return true; } bool DeleteSlot(const char* name) const { return DeleteSlotImpl(name); } bool RawDeleteSlot(const char* name) const { return DeleteSlotImpl(name); } template bool DeleteSlotImpl(const Object &key) const { sq_pushobject(vm, obj); sq_pushobject(vm, key.GetObject()); if (SQ_FAILED(raw ? sq_rawdeleteslot(vm, -2, false) : sq_deleteslot(vm, -2, false))) { sq_pop(vm, 1); return false; } sq_pop(vm, 1); return true; } bool DeleteSlot(const Object &key) const { return DeleteSlotImpl(key); } bool RawDeleteSlot(const Object &key) const { return DeleteSlotImpl(key); } SQInteger Length() const { return sq_obj_getsize(&obj); } bool Clear() { sq_pushobject(vm, GetObject()); bool ok = SQ_SUCCEEDED(sq_clear(vm, -1)); sq_pop(vm, 1); return ok; } protected: template void BindImpl(const char* name, V& v) { sq_pushobject(vm, GetObject()); sq_pushstring(vm, name, -1); sq_pushobject(vm, v.GetObject()); SQRAT_VERIFY(SQ_SUCCEEDED(sq_newslot(vm, -3, false))); sq_pop(vm,1); // pop table } }; class Table : public TableBase { public: Table(HSQUIRRELVM v) : TableBase(v) { SQRAT_ASSERT(v); sq_newtable(vm); SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm,-1,&obj))); sq_addref(vm, &obj); sq_pop(vm,1); } Table() { } Table(const Object& obj) : TableBase(obj) { } Table(Object && obj) : TableBase(SQRAT_STD::move(obj)) { } Table(HSQOBJECT o, HSQUIRRELVM v) : TableBase(o, v) { } }; class RootTable : public TableBase { public: RootTable(HSQUIRRELVM v) : TableBase(v) { sq_pushroottable(vm); SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm,-1,&obj))); sq_addref(vm, &obj); sq_pop(v,1); // pop root table } }; class RegistryTable : public TableBase { public: RegistryTable(HSQUIRRELVM v) : TableBase(v) { sq_pushregistrytable(v); SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm,-1,&obj))); sq_addref(vm, &obj); sq_pop(v,1); // pop the registry table } }; /// Used to get and push Table instances to and from the stack as references (tables are always references in Squirrel) template<> struct Var { Table value; ///< The actual value of get operations /// Attempts to get the value off the stack at idx as a Table Var(HSQUIRRELVM vm, SQInteger idx) { HSQOBJECT obj; sq_resetobject(&obj); SQRAT_VERIFY(SQ_SUCCEEDED(sq_getstackobj(vm,idx,&obj))); value = Table(obj, vm); SQObjectType value_type = sq_gettype(vm, idx); if (value_type != OT_TABLE && value_type != OT_NULL) { SQRAT_ASSERTF(0, FormatTypeError(vm, idx, "table").c_str()); } } /// Called by Sqrat::PushVar to put an Table reference on the stack static void push(HSQUIRRELVM vm, const Table& value) { HSQOBJECT obj; sq_resetobject(&obj); obj = value.GetObject(); sq_pushobject(vm,obj); } static const char * getVarTypeName() { return "table"; } static bool check_type(HSQUIRRELVM vm, SQInteger idx) { return sq_gettype(vm, idx) == OT_TABLE || sq_gettype(vm, idx) == OT_NULL; } }; template<> struct Var : Var
{Var(HSQUIRRELVM vm, SQInteger idx) : Var
(vm, idx) {}}; template<> struct Var : Var
{Var(HSQUIRRELVM vm, SQInteger idx) : Var
(vm, idx) {}}; } #endif ================================================ FILE: sqrat/include/sqrat/sqratTypes.h ================================================ // Sqrat: altered version by Gaijin Games KFT // SqratTypes: Type Translators // // // Copyright (c) 2009 Brandon Jones // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // #pragma once #if !defined(_SQRAT_TYPES_H_) #define _SQRAT_TYPES_H_ #include #include "sqratClassType.h" #include "sqratUtil.h" namespace Sqrat { template struct getAsInt { static bool getFromStack(HSQUIRRELVM vm, SQInteger idx, T &value) { static_assert(SQRAT_STD::is_convertible::value, "type is not convertible to int"); SQObjectType value_type = sq_gettype(vm, idx); switch(value_type) { case OT_BOOL: { SQBool sqValueb = SQFalse; SQRESULT res = sq_getbool(vm, idx, &sqValueb); value = static_cast(sqValueb); return SQ_SUCCEEDED(res); } case OT_INTEGER: { SQInteger sqValue = 0; SQRESULT res = sq_getinteger(vm, idx, &sqValue); value = static_cast(sqValue); return SQ_SUCCEEDED(res); } case OT_FLOAT: { SQFloat sqValuef = 0; SQRESULT res = sq_getfloat(vm, idx, &sqValuef); value = static_cast(static_cast(sqValuef)); return SQ_SUCCEEDED(res); } default: SQRAT_ASSERTF(0, FormatTypeError(vm, idx, "integer").c_str()); value = static_cast(0); break; } return false; } }; template struct getAsFloat { static bool getFromStack(HSQUIRRELVM vm, SQInteger idx, T& value) { static_assert(SQRAT_STD::is_convertible::value, "type is not convertible to float"); SQObjectType value_type = sq_gettype(vm, idx); switch(value_type) { case OT_BOOL: { SQBool sqValueb = SQFalse; SQRESULT res = sq_getbool(vm, idx, &sqValueb); value = static_cast(sqValueb); return SQ_SUCCEEDED(res); } case OT_INTEGER: { SQInteger sqValue = 0; SQRESULT res = sq_getinteger(vm, idx, &sqValue); value = static_cast(sqValue); return SQ_SUCCEEDED(res); } case OT_FLOAT: { SQFloat sqValuef = 0; SQRESULT res = sq_getfloat(vm, idx, &sqValuef); value = static_cast(sqValuef); return SQ_SUCCEEDED(res); } default: SQRAT_ASSERTF(0, FormatTypeError(vm, idx, "float").c_str()); value = 0; break; } return false; } }; ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Used to get and push class instances to and from the stack as copies /// /// \remarks /// This specialization requires T to have a default constructor. /// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template struct Var { using ClassT = ClassType>; T value; ///< The actual value of get operations /// Attempts to get the value off the stack at idx as the given type Var(HSQUIRRELVM vm, SQInteger idx) { //-V1077 T* ptr = ClassT::GetInstance(vm, idx); if (ptr != NULL) { value = *ptr; } } /// Called by Sqrat::PushVar to put a class object on the stack static void push(HSQUIRRELVM vm, const T& value) { if (ClassT::hasClassData(vm)) ClassT::PushInstanceCopy(vm, value); else SQRAT_ASSERTF(0, "Class/typename was not bound"); } static const char * getVarTypeName() { return ClassT::ClassName().c_str(); } static bool check_type(HSQUIRRELVM vm, SQInteger idx) { return ClassT::IsClassInstance(vm, idx); } }; // Forward-declare Var<> specializations for host project types (if any). // These turn missing-include bugs into compile errors instead of runtime asserts. #ifdef SQRAT_FORWARD_VAR_SPECIALIZATIONS SQRAT_FORWARD_VAR_SPECIALIZATIONS #endif // Var trait to find-out whether value can be used after Var has been // destroyed. If Var has user-provided destructor it likely controls value // lifetime template struct VarControlsValueLifeTime { enum {value = 0}; }; template SQFUNCTION SqGlobalThunk(); template struct Var>> { static void push(HSQUIRRELVM vm, const Func& value) { SQFUNCTION funcThunk = SqGlobalThunk(); SQUserPointer funcPtr = sq_newuserdata(vm, sizeof(Func)); new (funcPtr) Func(value); sq_setreleasehook(vm, -1, ImplaceFreeReleaseHook); sq_newclosure(vm, funcThunk, 1); } }; /// Used to get and push class instances to and from the stack as references template struct Var> && !SQRAT_STD::is_enum_v>>> { using ClassT = ClassType>; T& value; ///< The actual value of get operations /// Attempts to get the value off the stack at idx as the given type Var(HSQUIRRELVM vm, SQInteger idx) : value(*getCheckedInstance(vm, idx)) { } private: static T* getCheckedInstance(HSQUIRRELVM vm, SQInteger idx) { T* ptr = ClassT::GetInstance(vm, idx); if (!ptr) { // Return pointer to aligned dummy storage to avoid null dereference. // GetInstance already raised an assert; the caller should check for errors. // Uses raw storage instead of T{} to compile for non-default-constructible types. alignas(SQRAT_STD::remove_const_t) static char dummy[sizeof(SQRAT_STD::remove_const_t)] = {}; return reinterpret_cast(dummy); } return ptr; } public: /// Called by Sqrat::PushVarR to put a class object on the stack static void push(HSQUIRRELVM vm, T& value) { if (ClassT::hasClassData(vm)) { if constexpr (SQRAT_STD::is_const_v) ClassT::PushInstanceCopy(vm, value); else ClassT::PushNativeInstance(vm, const_cast*>(&value)); } else SQRAT_ASSERTF(0, "Class/typename was not bound"); } static const char * getVarTypeName() { return ClassT::ClassName().c_str(); } static bool check_type(HSQUIRRELVM vm, SQInteger idx) { return ClassT::IsClassInstance(vm, idx); } }; /// Used to get and push class instances to and from the stack as pointers template struct Var>> { using ClassT = ClassType>; T* value; ///< The actual value of get operations /// Attempts to get the value off the stack at idx as the given type Var(HSQUIRRELVM vm, SQInteger idx) : value(ClassT::GetInstance(vm, idx, true)) { } /// Direct extraction from HSQOBJECT -- stackless explicit Var(const HSQOBJECT &o) : value(ClassT::GetInstanceFromObj(o)) {} /// Called by Sqrat::PushVar to put a class object on the stack static void push(HSQUIRRELVM vm, T* value) { if (ClassT::hasClassData(vm)) ClassT::PushNativeInstance(vm, const_cast*>(value)); else SQRAT_ASSERTF(0, "Class/typename was not bound"); } static const char * getVarTypeName() { return ClassT::ClassName().c_str(); } static bool check_type(HSQUIRRELVM vm, SQInteger idx) { return ClassT::IsClassInstance(vm, idx); } }; /// Used to get (as copies) and push (as references) class instances to and from the stack as a shared_ptr template void PushVarR(HSQUIRRELVM vm, T& value); template struct Var > { SQRAT_STD::shared_ptr value; ///< The actual value of get operations /// Attempts to get the value off the stack at idx as the given type Var(HSQUIRRELVM vm, SQInteger idx) { if (sq_gettype(vm, idx) != OT_NULL) { Var instance(vm, idx); value.reset(new T(instance.value)); } } /// Called by Sqrat::PushVar to put a class object on the stack static void push(HSQUIRRELVM vm, const SQRAT_STD::shared_ptr& value) { PushVarR(vm, *value); } }; // Arithmetic types (integers and floats, excluding bool which has special handling) template struct Var && !SQRAT_STD::is_same_v>> { T value; Var(HSQUIRRELVM vm, SQInteger idx) { if constexpr (SQRAT_STD::is_integral_v) getAsInt::getFromStack(vm, idx, value); else getAsFloat::getFromStack(vm, idx, value); } explicit Var(const HSQOBJECT &o) { SQRAT_ASSERT(check_type(o)); if constexpr (SQRAT_STD::is_integral_v) value = static_cast(sq_objtointeger(&o)); else value = static_cast(sq_objtofloat(&o)); } static void push(HSQUIRRELVM vm, T value) { if constexpr (SQRAT_STD::is_integral_v) sq_pushinteger(vm, static_cast(value)); else sq_pushfloat(vm, static_cast(value)); } static const char * getVarTypeName() { return SQRAT_STD::is_integral_v ? "integer" : "float"; } static bool check_type(HSQUIRRELVM vm, SQInteger idx) { return sq_gettype(vm, idx) & SQOBJECT_NUMERIC; } static bool check_type(const HSQOBJECT &o) { return o._type & SQOBJECT_NUMERIC; } }; // Reference/const-reference forwarder for arithmetic types (including bool) template struct Var>>> : Var> { using Var>::Var; }; /////////////////////////////////////// /// Enums /////////////////////////////////////// template struct Var>> { T value; Var(HSQUIRRELVM vm, SQInteger idx) { SQInteger intVal = 0; if (SQ_SUCCEEDED(sq_getinteger(vm, idx, &intVal))) value = static_cast(intVal); } explicit Var(const HSQOBJECT &o) { SQRAT_ASSERT(check_type(o)); value = static_cast(sq_objtointeger(&o)); } static void push(HSQUIRRELVM vm, T value) { sq_pushinteger(vm, static_cast(value)); } static const char * getVarTypeName() { return "enum"; } static bool check_type(HSQUIRRELVM vm, SQInteger idx) { return sq_gettype(vm, idx) == OT_INTEGER; } static bool check_type(const HSQOBJECT &o) { return o._type == OT_INTEGER; } }; // Reference/const-reference forwarder for enum types template struct Var>>> : Var> { using Var>::Var; }; /// Used to get and push bools to and from the stack template<> struct Var { bool value; ///< The actual value of get operations /// Attempts to get the value off the stack at idx as a bool Var(HSQUIRRELVM vm, SQInteger idx) { SQBool sqValue; sq_tobool(vm, idx, &sqValue); value = (sqValue != 0); } explicit Var(const HSQOBJECT &o) { SQRAT_ASSERT(check_type(o)); value = sq_obj_is_true(&o) != 0; } /// Called by Sqrat::PushVar to put a bool on the stack static void push(HSQUIRRELVM vm, const bool& value) { sq_pushbool(vm, static_cast(value)); } static const char * getVarTypeName() { return "bool"; } static bool check_type(HSQUIRRELVM /*vm*/, SQInteger /*idx*/) { return true; } static bool check_type(const HSQOBJECT &) { return true; } }; // Trait: true for types with HSQOBJECT-based Var constructors (arithmetic + enum). // Used by GetSlotValueImpl to dispatch the stackless extraction path at compile time. template inline constexpr bool has_direct_var_v = SQRAT_STD::is_arithmetic_v || SQRAT_STD::is_enum_v; /// Var construction is deleted: it exposed non-const write access to VM-internal strings. /// Use Var or Var instead. push() is kept for PushVar(vm, ptr). template<> struct Var { Var() = delete; Var(HSQUIRRELVM, SQInteger) = delete; static void push(HSQUIRRELVM vm, const char* value, SQInteger len = -1) { sq_pushstring(vm, value, len); } static const char * getVarTypeName() { return "string"; } static bool check_type(HSQUIRRELVM vm, SQInteger idx) { return sq_gettype(vm, idx) == OT_STRING; } }; /// Used to get and push strings as char arrays to and from the stack template<> struct Var { private: HSQOBJECT obj = {}; /* hold a reference to the object holding value during the Var struct lifetime*/ HSQUIRRELVM v = nullptr; public: const char* value = ""; ///< The actual value of get operations SQInteger valueLen = 0; /// Attempts to get the value off the stack at idx as a character array Var(HSQUIRRELVM vm, SQInteger idx) { v = vm; if (SQ_SUCCEEDED(sq_tostring(vm, idx))) { sq_getstackobj(vm, -1, &obj); sq_getstringandsize(vm, -1, &value, &valueLen); sq_addref(vm, &obj); sq_pop(vm,1); } else { sq_resetobject(&obj); } } Var(Var const &rhs) : obj(rhs.obj) , v(rhs.v) , value(rhs.value) , valueLen(rhs.valueLen) { sq_addref(v, &obj); } Var &operator=(Var const &rhs) { if (&rhs == this) return *this; if (v && !sq_isnull(obj)) sq_release(v, &obj); obj = rhs.obj; v = rhs.v; value = rhs.value; valueLen = rhs.valueLen; sq_addref(v, &obj); return *this; } ~Var() { if(v && !sq_isnull(obj)) { sq_release(v, &obj); } } /// Called by Sqrat::PushVar to put a character array on the stack static void push(HSQUIRRELVM vm, const char* value, SQInteger len = -1) { sq_pushstring(vm, value, len); } static const char * getVarTypeName() { return "string"; } static bool check_type(HSQUIRRELVM vm, SQInteger idx) { return sq_gettype(vm, idx) == OT_STRING; } }; template<> struct VarControlsValueLifeTime { enum {value = 1}; }; /// Used to get and push strings to and from the stack template<> struct Var { string value; ///< The actual value of get operations /// Attempts to get the value off the stack at idx as a string Var(HSQUIRRELVM vm, SQInteger idx) { if (SQ_FAILED(sq_tostring(vm, idx))) return; const char* ret = nullptr; sq_getstring(vm, -1, &ret); value = string(ret, sq_getsize(vm, -1)); sq_pop(vm,1); } /// Called by Sqrat::PushVar to put a string on the stack static void push(HSQUIRRELVM vm, const string& value) { sq_pushstring(vm, value.c_str(), value.size()); } static const char * getVarTypeName() { return "string"; } static bool check_type(HSQUIRRELVM vm, SQInteger idx) { return sq_gettype(vm, idx) == OT_STRING; } }; /// Const string references are handled as copies (strings are always copied) template<> struct Var : Var { using Var::Var; }; /// Used to get and push strings as string_view objects template <> struct Var { private: HSQOBJECT obj = {}; /* hold a reference to the object holding value during the Var struct lifetime*/ HSQUIRRELVM v = nullptr; public: string_view value; Var(HSQUIRRELVM vm, SQInteger idx) { v = vm; if (SQ_SUCCEEDED(sq_tostring(vm, idx))) { sq_getstackobj(vm, -1, &obj); const char *strPtr = nullptr; SQInteger strLen = 0; sq_getstringandsize(vm, -1, &strPtr, &strLen); sq_addref(vm, &obj); sq_pop(vm, 1); value = string_view(strPtr, strLen); } else { sq_resetobject(&obj); } } ~Var() { if (v && !sq_isnull(obj)) sq_release(v, &obj); } Var(Var const &rhs) : obj(rhs.obj) , v(rhs.v) , value(rhs.value) { sq_addref(v, &obj); } Var &operator=(Var const &rhs) { if (&rhs == this) return *this; if (v && !sq_isnull(obj)) sq_release(v, &obj); obj = rhs.obj; v = rhs.v; value = rhs.value; sq_addref(v, &obj); return *this; } static void push(HSQUIRRELVM vm, string_view sv) { sq_pushstring(vm, sv.data(), sv.size()); } static const char *getVarTypeName() { return "string"; } static bool check_type(HSQUIRRELVM vm, SQInteger idx) { return sq_gettype(vm, idx) == OT_STRING; } }; template <> struct VarControlsValueLifeTime // Var holds reference to VM's string which is pointed by string_view { enum { value = 1 }; }; // Non-referencable type definitions template struct is_referencable : public SQRAT_STD::true_type {}; template struct is_referencable::value>> : public SQRAT_STD::false_type {}; #define SQRAT_MAKE_NONREFERENCABLE( type ) \ template<> struct is_referencable : public SQRAT_STD::false_type {}; SQRAT_MAKE_NONREFERENCABLE(string) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Pushes a value on to a given VM's stack /// /// \remarks /// What this function does is defined by Sqrat::Var template specializations, /// and thus you can create custom functionality for it by making new template specializations. /// When making a custom type that is not referencable, you must use SQRAT_MAKE_NONREFERENCABLE( type ) /// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template inline void PushVar(HSQUIRRELVM vm, T* value) { Var::push(vm, value); } ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Pushes a value on to a given VM's stack /// /// \remarks /// What this function does is defined by Sqrat::Var template specializations, /// and thus you can create custom functionality for it by making new template specializations. /// When making a custom type that is not referencable, you must use SQRAT_MAKE_NONREFERENCABLE( type ) /// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template inline void PushVar(HSQUIRRELVM vm, const T& value) { Var::push(vm, value); } /// Pushes a reference on to a given VM's stack (some types cannot be referenced and will be copied instead) template inline void PushVarR(HSQUIRRELVM vm, T& value) { if constexpr (!SQRAT_STD::is_pointer_v && is_referencable>::value) { Var::push(vm, value); } else if constexpr (SQRAT_STD::is_scalar_v) { PushVar(vm, value); } else { PushVar(vm, value); } } namespace vargs { template SQRAT_STD::tuple...> make_vars_i(HSQUIRRELVM vm, int idx, SQRAT_STD::index_sequence) { ((void)idx); ((void)vm); return SQRAT_STD::make_tuple(Var(vm, idx + Indeces)...); } template SQRAT_STD::tuple...> make_vars(HSQUIRRELVM vm, int idx) { return make_vars_i(vm, idx, SQRAT_STD::index_sequence_for()); } template bool check_var_types(HSQUIRRELVM vm, int idx) { if (!Var::check_type(vm, idx)) { const char *argTypeName = "unknown"; SQInteger prevTop = sq_gettop(vm); if (SQ_SUCCEEDED(sq_typeof(vm, idx)) && SQ_SUCCEEDED(sq_tostring(vm, -1))) { sq_getstring(vm, -1, &argTypeName); } char errMsg[128]; snprintf(errMsg, sizeof(errMsg), "Wrong argument type, expected '%s', got '%s'", Var::getVarTypeName(), argTypeName); sq_settop(vm, prevTop); (void)sq_throwerror(vm, errMsg); return false; } return true; } template bool check_var_types(HSQUIRRELVM vm, int idx, SQRAT_STD::enable_if_t<(sizeof...(Tail) > 0), bool> = false) { if (!check_var_types(vm, idx)) return false; return check_var_types(vm, idx+1); } template bool check_var_types(HSQUIRRELVM /*vm*/, int /*idx*/, SQRAT_STD::enable_if_t<(sizeof...(T) == 0), bool> = false) { return true; } } // utility for checking types of SquirrelFunc arguments template bool check_signature(HSQUIRRELVM vm, SQInteger start_stack_pos=1) { return vargs::check_var_types(vm, start_stack_pos); } } // namespace Sqrat #endif ================================================ FILE: sqrat/include/sqrat/sqratUtil.h ================================================ // Sqrat: altered version by Gaijin Games KFT // SqratUtil: Quirrel Utilities // // // Copyright (c) 2009 Brandon Jones // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // #pragma once #if !defined(_SQRAT_UTIL_H_) #define _SQRAT_UTIL_H_ #ifdef USE_SQRAT_CONFIG #include "sqratConfig.h" #else #include #define SQRAT_ASSERT assert #define SQRAT_ASSERTF(cond, msg, ...) assert((cond) && (msg)) #define SQRAT_VERIFY(cond) do { if (!(cond)) assert(#cond); } while(0) #endif #include #include #if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) #else #error C++17 support required #endif #if defined(SQRAT_HAS_SKA_HASH_MAP) # include #endif #ifndef SQRAT_STD #define SQRAT_STD std #endif #if defined(SQRAT_HAS_EASTL) # include # include # include # include # include # include # include EA_DISABLE_ALL_VC_WARNINGS() #else # include # include # include # include # include # include # include # include # include #endif namespace Sqrat { #if defined(SQRAT_HAS_EASTL) using string = eastl::basic_string; using string_view = eastl::basic_string_view; template using hash = eastl::hash; template using shared_ptr = eastl::shared_ptr; template using weak_ptr = eastl::weak_ptr; #else using string = std::basic_string; using string_view = std::basic_string_view; template using hash = std::hash; template using shared_ptr = std::shared_ptr; template using weak_ptr = std::weak_ptr; #endif //defined(SQRAT_HAS_EASTL) #if defined(SQRAT_HAS_EASTL) template using optional = eastl::optional; using nullopt_t = eastl::nullopt_t; inline constexpr eastl::nullopt_t nullopt{eastl::nullopt}; #else template using optional = std::optional; using nullopt_t = std::nullopt_t; inline constexpr std::nullopt_t nullopt{std::nullopt}; #endif #if defined(SQRAT_HAS_SKA_HASH_MAP) template > using class_hash_map = ska::flat_hash_map; template > using hash_set = ska::flat_hash_set; #elif defined(SQRAT_HAS_EASTL) template > using class_hash_map = eastl::unordered_map; template > using hash_set = eastl::unordered_set; #else template > using class_hash_map = std::unordered_map; template > using hash_set = std::unordered_set; #endif template void SQRAT_UNUSED(const T&) {} ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// /// Returns a string that has been formatted to give a nice type error message (for usage with Class::SquirrelFunc) /// /// \param vm VM the error occurred with /// \param idx Index on the stack of the argument that had a type error /// \param expectedType The name of the type that the argument should have been /// /// \return String containing a nice type error message /// ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// inline string FormatTypeError(HSQUIRRELVM vm, SQInteger idx, const char *expectedType) { SQInteger prevTop = sq_gettop(vm); const char* actualType = "unknown"; if (SQ_SUCCEEDED(sq_typeof(vm, idx))) { sq_tostring(vm, -1); sq_getstring(vm, -1, &actualType); } char buf[128]; snprintf(buf, sizeof(buf), "wrong type (%s expected, got %s)", expectedType, actualType); sq_settop(vm, prevTop); return string(buf); } /// Returns the last error that occurred with a Squirrel VM (not associated with Sqrat errors) inline string LastErrorString(HSQUIRRELVM vm) { const char* sqErr = "n/a"; sq_getlasterror(vm); if (sq_gettype(vm, -1) == OT_NULL) { sq_pop(vm, 1); return string(); } sq_tostring(vm, -1); sq_getstring(vm, -1, &sqErr); string res(sqErr); sq_pop(vm, 2); return res; } template SQInteger ImplaceFreeReleaseHook(HSQUIRRELVM, SQUserPointer p, SQInteger) { SQRAT_UNUSED(p); // for Obj without destructor static_cast(p)->~Obj(); return 1; } template struct Var; // utilities for manipulations with variadic templates arguments namespace vargs { #if defined(_MSC_VER) #pragma warning(push) #pragma warning(disable: 4100) #endif template auto apply_helper(Func pf, SQRAT_STD::index_sequence, Tuple &args) { return pf(SQRAT_STD::get(args).value...); } template auto apply(Func pf, Tuple &&args) { constexpr auto argsN = SQRAT_STD::tuple_size>::value; return apply_helper(pf, SQRAT_STD::make_index_sequence(), args); } template auto apply_member_helper(C *ptr, Member pf, SQRAT_STD::index_sequence, Tuple &args) { return (ptr->*pf)(SQRAT_STD::get(args).value...); } template auto apply_member(C *ptr, Member pf, Tuple &&args) { constexpr auto argsN = SQRAT_STD::tuple_size>::value; return apply_member_helper(ptr, pf, SQRAT_STD::make_index_sequence(), args); } #if defined(_MSC_VER) #pragma warning(pop) #endif } template using disjunction = SQRAT_STD::disjunction; template using void_t = SQRAT_STD::void_t; template struct is_function : disjunction, SQRAT_STD::is_function>>> { }; template using disable_if = SQRAT_STD::enable_if; template struct member_function_signature { using type = void; }; template struct member_function_signature { using type = R(A...); }; template struct member_function_signature { using type = R(A...); }; template struct member_function_signature { using type = R(A...); }; template struct member_function_signature { using type = R(A...); }; template struct member_function_signature { using type = R(A...); }; template struct member_function_signature { using type = R(A...); }; template struct member_function_signature { using type = R(A...); }; template struct member_function_signature { using type = R(A...); }; template using member_function_signature_t = typename member_function_signature::type; template struct get_class_callop_signature { using type = member_function_signature_t::operator())>; }; template using get_class_callop_signature_t = typename get_class_callop_signature::type; template struct get_function_signature { using type = void; }; template struct get_function_signature { using type = R(A...); }; template struct get_function_signature { using type = R(A...); }; template struct get_function_signature { using type = R(A...); }; template struct get_function_signature { using type = R(A...); }; template using get_function_signature_t = typename get_function_signature::type; template struct get_callable_function : SQRAT_STD::conditional_t< SQRAT_STD::is_member_function_pointer::value, member_function_signature, SQRAT_STD::conditional_t< is_function::value, get_function_signature>, SQRAT_STD::conditional_t< SQRAT_STD::is_class::value, get_class_callop_signature, void_t > > > { }; template using get_callable_function_t = typename get_callable_function::type; template struct result_of; template struct result_of { using type = R; }; template using result_of_t = typename result_of::type; template struct function_args_num; template struct function_args_num { static size_t const value = sizeof...(A); }; template constexpr size_t function_args_num_v = function_args_num::value; template struct has_call_operator { using yes = char[2]; using no = char[1]; struct Fallback { void operator()();}; struct Derived : T, Fallback { Derived() {} }; template static no& test(decltype(&U::operator())*); template static yes& test(...); static bool const value = sizeof(test(0)) == sizeof(yes); }; template struct is_callable : SQRAT_STD::conditional::value, has_call_operator, is_function>::type { }; template constexpr int is_callable_v = is_callable::value; template int SqGetArgCount() { return function_args_num_v>; } } #if defined(SQRAT_HAS_EASTL) EA_RESTORE_ALL_VC_WARNINGS() #endif #endif ================================================ FILE: sqrat/include/sqrat.h ================================================ // Sqrat: altered version by Gaijin Games KFT // Sqrat: Quirrel C++ Binding Utility // // // Copyright (c) 2009 Brandon Jones // // This software is provided 'as-is', without any express or implied // warranty. In no event will the authors be held liable for any damages // arising from the use of this software. // // Permission is granted to anyone to use this software for any purpose, // including commercial applications, and to alter it and redistribute it // freely, subject to the following restrictions: // // 1. The origin of this software must not be misrepresented; you must not // claim that you wrote the original software. If you use this software // in a product, an acknowledgment in the product documentation would be // appreciated but is not required. // // 2. Altered source versions must be plainly marked as such, and must not be // misrepresented as being the original software. // // 3. This notice may not be removed or altered from any source // distribution. // #pragma once #if !defined(_SQRAT_MAIN_H_) #define _SQRAT_MAIN_H_ #include "sqrat/sqratTable.h" #include "sqrat/sqratClass.h" #include "sqrat/sqratFunction.h" #include "sqrat/sqratConst.h" #include "sqrat/sqratUtil.h" #include "sqrat/sqratScript.h" #include "sqrat/sqratArray.h" #endif ================================================ FILE: sqstdlib/CMakeLists.txt ================================================ set(SQSTDLIB_SRC sqstdaux.cpp sqstdblob.cpp sqstddebug.cpp sqstdio.cpp sqstdhash.cpp sqstdmath.cpp sqstdrex.cpp sqstdstream.cpp sqstdstring.cpp sqstddatetime.cpp sqstdserialization.cpp sqstdsystem.cpp) add_library(sqstdlib STATIC ${SQSTDLIB_SRC}) add_library(squirrel::sqstdlib ALIAS sqstdlib) target_include_directories(sqstdlib PUBLIC "$" "$" PRIVATE "$" "$" "$" ) ================================================ FILE: sqstdlib/sqstdaux.cpp ================================================ /* see copyright notice in squirrel.h */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define ARRAY_ELEMENTS_IN_BRIEF_DUMP 4 #define TABLE_ELEMENTS_IN_BRIEF_DUMP 3 #ifdef SQ_STACK_DUMP_SECRET_PREFIX #define STRINGIZE(x) #x #define SQ_STRING_EXPAND(x) STRINGIZE(x) #define SECRET_PREFIX SQ_STRING_EXPAND(SQ_STACK_DUMP_SECRET_PREFIX) #endif template static void print_simple_value(HSQUIRRELVM v, PrintFunc pf, SQObjectPtr &val, bool string_quotes = true) { SQObjectPtr valStr; switch (sq_type(val)) { case OT_STRING: if (v->ToString(val, valStr)) { if (string_quotes) pf(v, "\"%s\"", _stringval(valStr)); else pf(v, "%s", _stringval(valStr)); } break; case OT_CLOSURE: if (v->ToString(_closure(val)->_function->_name, valStr)) pf(v, "FN:%s", _stringval(valStr)); break; case OT_NATIVECLOSURE: if (v->ToString(_nativeclosure(val)->_name, valStr)) pf(v, "FN:%s", _stringval(valStr)); break; case OT_TABLE: pf(v, "TABLE"); break; case OT_ARRAY: pf(v, "ARRAY"); break; case OT_CLASS: pf(v, "CLASS"); break; default: if (v->ToString(val, valStr)) pf(v, "%s", _stringval(valStr)); break; } } template static void collect_stack_string(HSQUIRRELVM v, PrintFunc pf) { SQStackInfos si; SQInteger i; SQFloat f; const char *s; SQInteger level=0; const char *name=0; SQInteger seq=0; pf(v,"\nCALLSTACK\n"); while(SQ_SUCCEEDED(sq_stackinfos(v,level,&si))) { if (si.line < 0 && level == 0) { // skip top native function ++level; continue; } const char *fn="unknown"; const char *src="unknown"; if(si.funcname)fn=si.funcname; if(si.source)src=si.source; pf(v,"*FUNCTION [%s()] %s:%d\n",fn,src,si.line); level++; } level=0; pf(v,"\nLOCALS\n"); for(level=0;level<10;level++){ seq=0; while((name = sq_getlocal(v,level,seq))) { seq++; #ifdef SQ_STACK_DUMP_SECRET_PREFIX bool should_keep_secret = (strncmp(name, SECRET_PREFIX, sizeof(SECRET_PREFIX)/sizeof(char) - 1) == 0); if (should_keep_secret) { sq_pop(v, 1); continue; } #endif switch(sq_gettype(v,-1)) { case OT_NULL: pf(v,"[%s] NULL\n",name); break; case OT_INTEGER: sq_getinteger(v,-1,&i); pf(v,"[%s] %" PRId64 "\n",name, int64_t(i)); break; case OT_FLOAT: sq_getfloat(v,-1,&f); pf(v,"[%s] %.14g\n",name,f); break; case OT_USERPOINTER: pf(v,"[%s] USERPOINTER\n",name); break; case OT_STRING: sq_getstring(v,-1,&s); pf(v,"[%s] \"%s\"\n",name,s); break; case OT_TABLE: { pf(v,"[%s] TABLE={",name); SQTable * t = _table(stack_get(v, -1)); SQObjectPtr refidx, key, val; SQInteger idx; SQInteger count = 0; while((idx = t->Next(false, refidx, key, val)) != -1) { refidx = idx; print_simple_value(v, pf, key, false); pf(v,"="); print_simple_value(v, pf, val); count++; if (count != t->CountUsed()) pf(v,", "); if (count + 1 > TABLE_ELEMENTS_IN_BRIEF_DUMP && count != t->CountUsed()) { pf(v,"...", int(t->CountUsed())); break; } } if (t->CountUsed() > TABLE_ELEMENTS_IN_BRIEF_DUMP) pf(v,"} (%d)\n", int(t->CountUsed())); else pf(v,"}\n"); } break; case OT_ARRAY: { pf(v,"[%s] ARRAY=[",name); SQArray * a = _array(stack_get(v, -1)); SQObjectPtr val; for (SQInteger i = 0; i < a->Size(); i++) { a->Get(i, val); print_simple_value(v, pf, val); if (i + 1 < a->Size()) { pf(v,", "); if (i > ARRAY_ELEMENTS_IN_BRIEF_DUMP - 2) { pf(v,"..."); break; } } } if (a->Size() > ARRAY_ELEMENTS_IN_BRIEF_DUMP) pf(v,"] (%d)\n", int(a->Size())); else pf(v,"]\n",name); } break; case OT_CLOSURE: pf(v,"[%s] CLOSURE=",name); print_simple_value(v, pf, stack_get(v, -1)); pf(v,"\n"); break; case OT_NATIVECLOSURE: pf(v,"[%s] NATIVECLOSURE=",name); print_simple_value(v, pf, stack_get(v, -1)); pf(v,"\n"); break; case OT_GENERATOR: pf(v,"[%s] GENERATOR\n",name); break; case OT_USERDATA: pf(v,"[%s] USERDATA\n",name); break; case OT_THREAD: pf(v,"[%s] THREAD\n",name); break; case OT_CLASS: pf(v,"[%s] CLASS\n",name); break; case OT_INSTANCE: pf(v,"[%s] INSTANCE=",name); print_simple_value(v, pf, stack_get(v, -1)); pf(v,"\n"); break; case OT_WEAKREF: pf(v,"[%s] WEAKREF\n",name); break; case OT_BOOL:{ SQBool bval; sq_getbool(v,-1,&bval); pf(v,"[%s] %s\n",name,bval == SQTrue ? "true":"false"); break; } default: assert(0); break; } sq_pop(v,1); } } } void sqstd_printcallstack(HSQUIRRELVM v) { SQPRINTFUNCTION pf = sq_geterrorfunc(v); if (pf) collect_stack_string(v, pf); } SQRESULT sqstd_formatcallstackstring(HSQUIRRELVM v) { int memlen = 128; SQAllocContext alloc_ctx = sq_getallocctx(v); char* mem = (char*)sq_malloc(alloc_ctx, memlen*sizeof(char)); if (!mem) return sq_throwerror(v, "Cannot allocate memory"); char* dst = mem; collect_stack_string(v, [alloc_ctx, &mem, &dst, &memlen](HSQUIRRELVM, const char *fmt, ...) { const int appendBlock = 128; va_list args; va_list argsCopy; va_start(args, fmt); va_copy(argsCopy, args); int nappend = vsnprintf(0, 0, fmt, argsCopy) + 1; va_end(argsCopy); int poffset = int(dst - mem); int memleft = memlen - poffset; if (memleft < nappend) { int nrequire = nappend - memleft; int newlen = memlen + ((nrequire / appendBlock) + 1) * appendBlock; char *newmem = (char *)sq_realloc(alloc_ctx, mem, memlen*sizeof(char), newlen*sizeof(char)); if (!newmem) return; mem = newmem; memlen = newlen; dst = mem + poffset; } dst += vsnprintf(dst, memlen - poffset, fmt, args); va_end(args); }); sq_pushstring(v, mem, dst-mem); sq_free(alloc_ctx, mem, memlen); return SQ_OK; } static SQInteger _sqstd_aux_printerror(HSQUIRRELVM v) { SQPRINTFUNCTION pf = sq_geterrorfunc(v); if(pf) { const char *sErr = 0; if(sq_gettop(v)>=1) { if(SQ_SUCCEEDED(sq_getstring(v,2,&sErr))) { pf(v,"\nAN ERROR HAS OCCURRED [%s]\n",sErr); } else{ pf(v,"\nAN ERROR HAS OCCURRED [unknown]\n"); } sqstd_printcallstack(v); } } return 0; } void _sqstd_compiler_message(HSQUIRRELVM v,SQMessageSeverity severity,const char *sErr,const char *sSource,SQInteger line,SQInteger column, const char *extra) { SQPRINTFUNCTION pf = sq_geterrorfunc(v); if(pf) { pf(v, "%s\n", sErr); pf(v, "%s:%d:%d\n",sSource,(int)line,(int)column); if (extra) pf(v, "%s\n", extra); } } void sqstd_seterrorhandlers(HSQUIRRELVM v) { sq_setcompilererrorhandler(v,_sqstd_compiler_message); sq_newclosure(v,_sqstd_aux_printerror,0); sq_seterrorhandler(v); } SQRESULT sqstd_throwerrorf(HSQUIRRELVM v,const char *err,...) { SQInteger n=256; va_list args; begin: va_start(args,err); char *b=sq_getscratchpad(v,n); SQInteger r=vsnprintf(b,n,err,args); va_end(args); if (r>=n) { n=r+1;//required+null goto begin; } else if (r<0) { return sq_throwerror(v,"@failed to generate formatted error message"); } else { return sq_throwerror(v,b); } } ================================================ FILE: sqstdlib/sqstdblob.cpp ================================================ /* see copyright notice in squirrel.h */ #include #include #include #include #include #include #include "sqstdstream.h" #include "sqstdblobimpl.h" #define SQSTD_BLOB_TYPE_TAG ((SQUnsignedInteger)(SQSTD_STREAM_TYPE_TAG | 0x00000002)) //Blob #define SETUP_BLOB(v) \ SQBlob *self = NULL; \ { if(SQ_FAILED(sq_getinstanceup(v,1,(SQUserPointer*)&self,(SQUserPointer)SQSTD_BLOB_TYPE_TAG))) \ return sq_throwerror(v,"invalid type tag"); } \ if(!self || !self->IsValid()) \ return sq_throwerror(v,"the blob is invalid"); static SQInteger _blob_resize(HSQUIRRELVM v) { SETUP_BLOB(v); SQInteger size; sq_getinteger(v,2,&size); if(!self->Resize(size)) return sqstd_throwerrorf(v,"resize failed, cur size=%d, requested=%d", int(self->Len()), int(size)); return 0; } static void __swap_dword(unsigned int *n) { *n=(unsigned int)(((*n&0xFF000000)>>24) | ((*n&0x00FF0000)>>8) | ((*n&0x0000FF00)<<8) | ((*n&0x000000FF)<<24)); } static void __swap_word(unsigned short *n) { *n=(unsigned short)((*n>>8)&0x00FF)| ((*n<<8)&0xFF00); } static SQInteger _blob_swap4(HSQUIRRELVM v) { SETUP_BLOB(v); SQInteger num=(self->Len()-(self->Len()%4))>>2; unsigned int *t=(unsigned int *)self->GetBuf(); for(SQInteger i = 0; i < num; i++) { __swap_dword(&t[i]); } return 0; } static SQInteger _blob_swap2(HSQUIRRELVM v) { SETUP_BLOB(v); SQInteger num=(self->Len()-(self->Len()%2))>>1; unsigned short *t = (unsigned short *)self->GetBuf(); for(SQInteger i = 0; i < num; i++) { __swap_word(&t[i]); } return 0; } static SQInteger _blob__set(HSQUIRRELVM v) { SETUP_BLOB(v); SQInteger idx,val; sq_getinteger(v,2,&idx); sq_getinteger(v,3,&val); if(idx < 0 || idx >= self->Len()) return sq_throwerror(v,"index out of range"); ((unsigned char *)self->GetBuf())[idx] = (unsigned char) val; sq_push(v,3); return 1; } static SQInteger _blob__get(HSQUIRRELVM v) { SETUP_BLOB(v); SQInteger idx; if ((sq_gettype(v, 2) & SQOBJECT_NUMERIC) == 0) { sq_pushnull(v); return sq_throwobject(v); } sq_getinteger(v,2,&idx); if(idx < 0 || idx >= self->Len()) return sq_throwerror(v,"index out of range"); sq_pushinteger(v,((unsigned char *)self->GetBuf())[idx]); return 1; } static SQInteger _blob__nexti(HSQUIRRELVM v) { SETUP_BLOB(v); if(sq_gettype(v,2) == OT_NULL) { sq_pushinteger(v, 0); return 1; } SQInteger idx; if(SQ_SUCCEEDED(sq_getinteger(v, 2, &idx))) { if(idx+1 < self->Len()) { sq_pushinteger(v, idx+1); return 1; } sq_pushnull(v); return 1; } return sq_throwerror(v,"internal error (_nexti) wrong argument type"); } static SQInteger _blob__typeof(HSQUIRRELVM v) { sq_pushstring(v,"blob",-1); return 1; } static SQInteger _blob_releasehook(HSQUIRRELVM SQ_UNUSED_ARG(vm), SQUserPointer p, SQInteger SQ_UNUSED_ARG(size)) { SQBlob *self = (SQBlob*)p; SQAllocContext alloc_ctx = self->_alloc_ctx; self->~SQBlob(); sq_free(alloc_ctx, self, sizeof(SQBlob)); return 1; } static SQInteger _blob_constructor(HSQUIRRELVM v) { SQInteger nparam = sq_gettop(v); SQInteger size = 0; if(nparam == 2) { sq_getinteger(v, 2, &size); } if(size < 0) return sq_throwerror(v, "cannot create blob with negative size"); //SQBlob *b = new SQBlob(size); SQAllocContext alloc_ctx = sq_getallocctx(v); SQBlob *b = new (sq_malloc(alloc_ctx, sizeof(SQBlob)))SQBlob(alloc_ctx, size); if(SQ_FAILED(sq_setinstanceup(v,1,b))) { b->~SQBlob(); sq_free(alloc_ctx,b,sizeof(SQBlob)); return sq_throwerror(v, "cannot create blob"); } sq_setreleasehook(v,1,_blob_releasehook); return 0; } static SQInteger _blob__cloned(HSQUIRRELVM v) { SQBlob *other = NULL; { if(SQ_FAILED(sq_getinstanceup(v,2,(SQUserPointer*)&other,(SQUserPointer)SQSTD_BLOB_TYPE_TAG))) return SQ_ERROR; } //SQBlob *thisone = new SQBlob(other->Len()); SQAllocContext alloc_ctx = sq_getallocctx(v); SQBlob *thisone = new (sq_malloc(alloc_ctx,sizeof(SQBlob))) SQBlob(alloc_ctx, other->Len()); memcpy(thisone->GetBuf(),other->GetBuf(),thisone->Len()); if(SQ_FAILED(sq_setinstanceup(v,1,thisone))) { thisone->~SQBlob(); sq_free(alloc_ctx,thisone,sizeof(SQBlob)); return sq_throwerror(v, "cannot clone blob"); } sq_setreleasehook(v,1,_blob_releasehook); return 0; } static SQInteger _blob_as_string(HSQUIRRELVM v) { SETUP_BLOB(v); sq_pushstring(v, (const char *)self->GetBuf(), self->Len() / sizeof(char)); return 1; } static const SQRegFunctionFromStr _blob_methods[] = { { _blob_constructor, "constructor([size: int]): instance", "Creates a blob of the given size (default 0)" }, { _blob_resize, "instance.resize(size: int)", "Resizes the blob to the given size" }, { _blob_swap2, "instance.swap2()", "Byte-swaps the blob contents as an array of 16-bit values" }, { _blob_swap4, "instance.swap4()", "Byte-swaps the blob contents as an array of 32-bit values" }, { _blob_as_string, "instance.as_string(): string", "Returns the blob contents as a string" }, { _blob__set, "instance._set(idx: int, val: int): int", "Sets the byte at the given index" }, { _blob__get, "instance._get(idx: int): int", "Returns the byte at the given index" }, { _blob__typeof, "instance._typeof(): string", "Returns 'blob'" }, { _blob__nexti, "instance._nexti(prev): int|null", "Iterator support: returns the next index or null" }, { _blob__cloned, "instance._cloned(other: instance)", "Clones the given blob into this instance" }, { NULL, NULL, NULL } }; //GLOBAL FUNCTIONS static SQInteger _g_blob_casti2f(HSQUIRRELVM v) { SQInteger i; sq_getinteger(v,2,&i); sq_pushfloat(v,*((const SQFloat *)&i)); return 1; } static SQInteger _g_blob_castf2i(HSQUIRRELVM v) { SQFloat f; sq_getfloat(v,2,&f); SQInteger result = 0; memcpy(&result, &f, sizeof(f)); sq_pushinteger(v, result); return 1; } static SQInteger _g_blob_swap2(HSQUIRRELVM v) { SQInteger i; sq_getinteger(v,2,&i); unsigned short s = (unsigned short)i; sq_pushinteger(v, ((s << 8) | ((s >> 8) & 0x00FFu)) & 0xFFFFu); return 1; } static SQInteger _g_blob_swap4(HSQUIRRELVM v) { SQInteger i; sq_getinteger(v,2,&i); unsigned int t4 = (unsigned int)i; __swap_dword(&t4); sq_pushinteger(v,(SQInteger)t4); return 1; } static SQInteger _g_blob_swapfloat(HSQUIRRELVM v) { SQFloat f; sq_getfloat(v,2,&f); __swap_dword((unsigned int *)&f); sq_pushfloat(v,f); return 1; } static const SQRegFunctionFromStr bloblib_funcs[] = { { _g_blob_casti2f, "pure casti2f(i: int): float", "Reinterprets the bits of an integer as a float" }, { _g_blob_castf2i, "pure castf2i(f: number): int", "Reinterprets the bits of a float as an integer" }, { _g_blob_swap2, "pure swap2(val: number): int", "Byte-swaps a 16-bit value" }, { _g_blob_swap4, "pure swap4(val: number): int", "Byte-swaps a 32-bit value" }, { _g_blob_swapfloat, "pure swapfloat(val: number): float", "Byte-swaps the bits of a float" }, { NULL, NULL, NULL } }; SQRESULT sqstd_getblob(HSQUIRRELVM v,SQInteger idx,SQUserPointer *ptr) { SQBlob *blob; if(SQ_FAILED(sq_getinstanceup(v,idx,(SQUserPointer *)&blob,(SQUserPointer)SQSTD_BLOB_TYPE_TAG))) return -1; *ptr = blob->GetBuf(); return SQ_OK; } SQInteger sqstd_getblobsize(HSQUIRRELVM v,SQInteger idx) { SQBlob *blob; if(SQ_FAILED(sq_getinstanceup(v,idx,(SQUserPointer *)&blob,(SQUserPointer)SQSTD_BLOB_TYPE_TAG))) return -1; return blob->Len(); } SQUserPointer sqstd_createblob(HSQUIRRELVM v, SQInteger size) { SQInteger top = sq_gettop(v); sq_pushregistrytable(v); sq_pushstring(v,"std_blob",-1); if(SQ_SUCCEEDED(sq_get(v,-2))) { sq_remove(v,-2); //removes the registry sq_push(v,1); // push the this sq_pushinteger(v,size); //size SQBlob *blob = NULL; if(SQ_SUCCEEDED(sq_call(v,2,SQTrue,SQFalse)) && SQ_SUCCEEDED(sq_getinstanceup(v,-1,(SQUserPointer *)&blob,(SQUserPointer)SQSTD_BLOB_TYPE_TAG))) { sq_remove(v,-2); return blob->GetBuf(); } } sq_settop(v,top); return NULL; } SQRESULT sqstd_register_bloblib(HSQUIRRELVM v) { return declare_stream(v,"blob",(SQUserPointer)SQSTD_BLOB_TYPE_TAG,"std_blob",_blob_methods,bloblib_funcs); } ================================================ FILE: sqstdlib/sqstdblobimpl.h ================================================ /* see copyright notice in squirrel.h */ #pragma once struct SQBlob : public SQStream { SQBlob(SQAllocContext alloc_ctx, SQInteger size) : _alloc_ctx(alloc_ctx) { _size = size; _allocated = size; _buf = (unsigned char *)sq_malloc(_alloc_ctx, size); memset(_buf, 0, _size); _ptr = 0; } virtual ~SQBlob() { sq_free(_alloc_ctx, _buf, _allocated); } SQInteger Write(const void *buffer, SQInteger size) override { if(!CanAdvance(size)) { GrowBufOf(_ptr + size - _size); } memcpy(&_buf[_ptr], buffer, size); _ptr += size; return size; } SQInteger Read(void *buffer,SQInteger size) override { SQInteger n = size; if(!CanAdvance(size)) { if((_size - _ptr) > 0) n = _size - _ptr; else return 0; } memcpy(buffer, &_buf[_ptr], n); _ptr += n; return n; } bool Resize(SQInteger n) { if (n < 0) return false; if(n != _allocated) { unsigned char *newbuf = (unsigned char *)sq_malloc(_alloc_ctx, n); memset(newbuf,0,n); if(_size > n) memcpy(newbuf,_buf,n); else memcpy(newbuf,_buf,_size); sq_free(_alloc_ctx, _buf,_allocated); _buf=newbuf; _allocated = n; if(_size > _allocated) _size = _allocated; if(_ptr > _allocated) _ptr = _allocated; } return true; } void GrowBufOf(SQInteger n) { if(_size + n > _allocated) { if(_size + n > _size * 2) Resize(_size + n); else Resize(_size * 2); } _size = _size + n; } bool CanAdvance(SQInteger n) { if(_ptr+n>_size)return false; return true; } SQInteger Seek(SQInteger offset, SQInteger origin) override { switch(origin) { case SQ_SEEK_SET: if(offset > _size || offset < 0) return -1; _ptr = offset; break; case SQ_SEEK_CUR: if(_ptr + offset > _size || _ptr + offset < 0) return -1; _ptr += offset; break; case SQ_SEEK_END: if(_size + offset > _size || _size + offset < 0) return -1; _ptr = _size + offset; break; default: return -1; } return 0; } bool IsValid() override { return _size == 0 || _buf?true:false; } bool EOS() override { return _ptr == _size; } SQInteger Flush() override { return 0; } SQInteger Tell() override { return _ptr; } SQInteger Len() override { return _size; } SQUserPointer GetBuf(){ return _buf; } public: SQAllocContext _alloc_ctx; private: SQInteger _size; SQInteger _allocated; SQInteger _ptr; unsigned char *_buf; }; ================================================ FILE: sqstdlib/sqstddatetime.cpp ================================================ /* see copyright notice in squirrel.h */ #include #include #include #include #include static SQInteger _datetime_clock(HSQUIRRELVM v) { sq_pushfloat(v,((SQFloat)clock())/(SQFloat)CLOCKS_PER_SEC); return 1; } static SQInteger _datetime_time(HSQUIRRELVM v) { SQInteger t = (SQInteger)time(NULL); sq_pushinteger(v,t); return 1; } static void _set_integer_slot(HSQUIRRELVM v,const char *name,SQInteger val) { sq_pushstring(v,name,-1); sq_pushinteger(v,val); sq_rawset(v,-3); } static SQInteger _datetime_date(HSQUIRRELVM v) { time_t t; SQInteger it; SQInteger format = 'l'; if(sq_gettop(v) > 1) { sq_getinteger(v,2,&it); t = it; if(sq_gettop(v) > 2) { sq_getinteger(v,3,(SQInteger*)&format); } } else { time(&t); } tm *date; if(format == 'u') date = gmtime(&t); else date = localtime(&t); if(!date) return sq_throwerror(v,"crt api failure"); sq_newtable(v); _set_integer_slot(v, "sec", date->tm_sec); _set_integer_slot(v, "min", date->tm_min); _set_integer_slot(v, "hour", date->tm_hour); _set_integer_slot(v, "day", date->tm_mday); _set_integer_slot(v, "month", date->tm_mon); _set_integer_slot(v, "year", date->tm_year+1900); _set_integer_slot(v, "wday", date->tm_wday); _set_integer_slot(v, "yday", date->tm_yday); return 1; } static const SQRegFunctionFromStr datetimelib_funcs[] = { { _datetime_clock, "pure clock(): float", "Returns CPU time used by the process in seconds" }, { _datetime_time, "time(): int", "Returns the current time as seconds since the Unix epoch" }, { _datetime_date, "date([time: int, format: int]): table", "Returns a table with date fields (sec,min,hour,day,month,year,wday,yday); format 'l' for local, 'u' for UTC" }, { NULL, NULL, NULL } }; SQRESULT sqstd_register_datetimelib(HSQUIRRELVM v) { SQInteger i = 0; while (datetimelib_funcs[i].f) { sq_new_closure_slot_from_decl_string(v, datetimelib_funcs[i].f, 0, datetimelib_funcs[i].declstring, datetimelib_funcs[i].docstring); i++; } return SQ_OK; } ================================================ FILE: sqstdlib/sqstddebug.cpp ================================================ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static SQInteger debug_seterrorhandler(HSQUIRRELVM v) { sq_seterrorhandler(v); return 0; } static SQInteger debug_setdebughook(HSQUIRRELVM v) { sq_setdebughook(v); return 0; } extern SQInteger __sq_getcallstackinfos(HSQUIRRELVM v, SQInteger level); static SQInteger debug_getstackinfos(HSQUIRRELVM v) { SQInteger level; sq_getinteger(v, -1, &level); return __sq_getcallstackinfos(v, level); } static SQInteger debug_getlocals(HSQUIRRELVM v) { SQInteger level = 1; SQBool includeInternal = false; if (sq_gettop(v) >= 2) sq_getinteger(v, 2, &level); if (sq_gettop(v) >= 3) sq_getbool(v, 3, &includeInternal); sq_newtable(v); const char *name = NULL; SQInteger seq=0; SQInteger prevTop = sq_gettop(v); while ((name = sq_getlocal(v, level, seq))) { ++seq; if (!includeInternal && (name[0] == '@' || strcmp(name, "this")==0 || strcmp(name, "vargv")==0)) { sq_pop(v, 1); continue; } sq_pushstring(v, name, -1); sq_push(v, -2); sq_newslot(v, -4, SQFalse); sq_pop(v, 1); } return 1; } static SQInteger debug_get_stack_top(HSQUIRRELVM v) { sq_pushinteger(v, sq_gettop(v)); return 1; } #ifndef NO_GARBAGE_COLLECTOR static SQInteger debug_collectgarbage(HSQUIRRELVM v) { sq_pushinteger(v, sq_collectgarbage(v)); return 1; } static SQInteger debug_resurrectunreachable(HSQUIRRELVM v) { sq_resurrectunreachable(v); return 1; } #endif static SQInteger debug_getbuildinfo(HSQUIRRELVM v) { sq_newtable(v); sq_pushstring(v, "version", -1); sq_pushstring(v, SQUIRREL_VERSION, -1); sq_newslot(v, -3, SQFalse); sq_pushstring(v, "charsize", -1); sq_pushinteger(v, sizeof(char)); sq_newslot(v, -3, SQFalse); sq_pushstring(v, "intsize", -1); sq_pushinteger(v, sizeof(SQInteger)); sq_newslot(v, -3, SQFalse); sq_pushstring(v, "floatsize", -1); sq_pushinteger(v, sizeof(SQFloat)); sq_newslot(v, -3, SQFalse); sq_pushstring(v, "gc", -1); #ifndef NO_GARBAGE_COLLECTOR sq_pushstring(v, "enabled", -1); #else sq_pushstring(v, "disabled", -1); #endif // NO_GARBAGE_COLLECTOR sq_newslot(v, -3, SQFalse); return 1; } static SQInteger debug_doc(HSQUIRRELVM v) { HSQOBJECT subject; sq_getstackobj(v, 2, &subject); SQObjectPtr value; SQObjectPtr key; key._type = OT_USERPOINTER; switch(sq_type(subject)) { case OT_CLOSURE: key._unVal.pUserPointer = (void *)_closure(subject)->_function; break; case OT_NATIVECLOSURE: key._unVal.pUserPointer = (void *)_nativeclosure(subject)->_function; break; case OT_INSTANCE: key._unVal.pUserPointer = (void *)_instance(subject)->_class; break; default: key._unVal.pUserPointer = subject._unVal.pUserPointer; break; } if (!_table(_ss(v)->doc_objects)->Get(key, value)) { sq_pushnull(v); return 1; } sq_pushobject(v, value); return 1; } static int nparamscheck_to_required_args(int nparamscheck) { if (nparamscheck==0) // No param check return 0; return abs(nparamscheck)-1; // Don't include `this` } static SQInteger debug_get_function_info_table(HSQUIRRELVM v) { HSQOBJECT subject; sq_getstackobj(v, 2, &subject); SQObjectPtr nullVal; SQObjectPtr docObject; SQObjectPtr value; SQObjectPtr key; key._type = OT_USERPOINTER; SQFunctionType ft(_ss(v)); bool initialized = false; bool native = false; switch (sq_type(subject)) { case OT_CLOSURE: { SQFunctionProto *f = _closure(subject)->_function; key._unVal.pUserPointer = (void *)_closure(subject)->_function; _table(_ss(v)->doc_objects)->Get(key, docObject); ft.functionName = f->_name; ft.returnTypeMask = f->_result_type_mask; ft.objectTypeMask = f->_param_type_masks[0]; ft.requiredArgs = f->_nparameters - f->_ndefaultparams - 1; ft.pure = f->_purefunction; ft.nodiscard = f->_nodiscard; int cnt = f->_nparameters - (f->_varparams ? 1 : 0); for (SQInteger n = 1; n < cnt; n++) { ft.argNames.push_back(f->_parameters[n]); ft.argTypeMask.push_back(f->_param_type_masks[n]); ft.defaultValues.push_back(nullVal); // TODO: defaultValues } if (f->_varparams) { ft.ellipsisArgTypeMask = f->_param_type_masks[f->_nparameters - 1]; } initialized = true; break; } case OT_NATIVECLOSURE: { native = true; key._unVal.pUserPointer = (void *)_nativeclosure(subject)->_function; _table(_ss(v)->doc_objects)->Get(key, docObject); key._unVal.pUserPointer = (void *)((size_t)(void *)_nativeclosure(subject)->_function ^ ~size_t(0)); if (_table(_ss(v)->doc_objects)->Get(key, value)) { SQInteger errorPos = -1; SQObjectPtr errorString; if (sq_isstring(value)) { if (sq_parse_function_type_string(v, _stringval(value), ft, errorPos, errorString)) { initialized = true; } else { // TODO: raise errorString } } break; } else { SQNativeClosure *f = _nativeclosure(subject); ft.functionName = f->_name; ft.returnTypeMask = ~0u; ft.objectTypeMask = f->_typecheck.size() > 0 ? f->_typecheck[0] : (_RT_INSTANCE | _RT_TABLE | _RT_CLASS | _RT_USERDATA | _RT_NULL); ft.requiredArgs = nparamscheck_to_required_args(f->_nparamscheck); ft.pure = f->_purefunction; ft.nodiscard = f->_nodiscard; int cnt = (ft.requiredArgs > f->_typecheck.size()) ? ft.requiredArgs : f->_typecheck.size(); for (SQInteger n = 1; n < cnt; n++) { char buf[16] = { 0 }; snprintf(buf, sizeof(buf), "arg%d", int(n)); ft.argNames.push_back(SQObjectPtr(SQString::Create(_ss(v), buf, -1))); ft.argTypeMask.push_back(n < f->_typecheck.size() ? f->_typecheck[n] : ~0u); ft.defaultValues.push_back(nullVal); // TODO: defaultValues } if (f->_nparamscheck < 0) { ft.ellipsisArgTypeMask = ~0u; } initialized = true; break; } } default: break; } if (!initialized) { sq_pushnull(v); return 1; } SQTable *res = SQTable::Create(_ss(v), 12); #define SET_SLOT(name, value) \ res->NewSlot(SQObjectPtr(SQString::Create(_ss(v), name, -1)), SQObjectPtr(value)) SET_SLOT("functionName", ft.functionName); SET_SLOT("returnTypeMask", (int)ft.returnTypeMask); SET_SLOT("objectTypeMask", (int)ft.objectTypeMask); SET_SLOT("ellipsisArgTypeMask", (int)ft.ellipsisArgTypeMask); SET_SLOT("requiredArgs", ft.requiredArgs); SET_SLOT("native", native); SET_SLOT("pure", ft.pure); SET_SLOT("nodiscard", ft.nodiscard); SET_SLOT("doc", docObject); SQObjectPtr argNames(SQArray::Create(_ss(v), ft.argNames.size())); for (SQInteger n = 0; n < ft.argNames.size(); n++) _array(argNames)->Set((SQInteger)n, ft.argNames[n]); SET_SLOT("argNames", argNames); SQObjectPtr argTypeMask(SQArray::Create(_ss(v), ft.argTypeMask.size())); for (SQInteger n = 0; n < ft.argTypeMask.size(); n++) _array(argTypeMask)->Set((SQInteger)n, SQObjectPtr((int)ft.argTypeMask[n])); SET_SLOT("argTypeMask", argTypeMask); /*SQObjectPtr defaultValues(SQArray::Create(_ss(v), ft.defaultValues.size())); for (SQInteger n = 0; n < ft.defaultValues.size(); n++) _array(defaultValues)->Set((SQInteger)n, ft.defaultValues[n]); SET_SLOT("defaultValues", defaultValues);*/ #undef SET_SLOT v->Push(SQObjectPtr(res)); return 1; } static SQInteger debug_get_function_decl_string(HSQUIRRELVM v) { HSQOBJECT subject; sq_getstackobj(v, 2, &subject); SQObjectPtr nullVal; SQObjectPtr value; SQObjectPtr key; key._type = OT_USERPOINTER; switch (sq_type(subject)) { case OT_CLOSURE: { SQFunctionProto *f = _closure(subject)->_function; SQFunctionType ft(_ss(v)); ft.functionName = f->_name; ft.returnTypeMask = f->_result_type_mask; ft.objectTypeMask = f->_param_type_masks[0]; ft.requiredArgs = f->_nparameters - f->_ndefaultparams - 1; ft.pure = f->_purefunction; ft.nodiscard = f->_nodiscard; int cnt = f->_nparameters - (f->_varparams ? 1 : 0); for (SQInteger n = 1; n < cnt; n++) { ft.argNames.push_back(f->_parameters[n]); ft.argTypeMask.push_back(f->_param_type_masks[n]); ft.defaultValues.push_back(nullVal); // TODO: defaultValues } if (f->_varparams) { ft.ellipsisArgTypeMask = f->_param_type_masks[f->_nparameters - 1]; } sq_pushobject(v, sq_stringify_function_type(v, ft)); return 1; } case OT_NATIVECLOSURE: key._unVal.pUserPointer = (void *)((size_t)(void *)_nativeclosure(subject)->_function ^ ~size_t(0)); if (_table(_ss(v)->doc_objects)->Get(key, value)) { sq_pushobject(v, value); return 1; } else { SQNativeClosure *f = _nativeclosure(subject); SQFunctionType ft(_ss(v)); ft.functionName = f->_name; ft.returnTypeMask = ~0u; ft.objectTypeMask = f->_typecheck.size() > 0 ? f->_typecheck[0] : (_RT_INSTANCE | _RT_TABLE | _RT_CLASS | _RT_USERDATA | _RT_NULL); ft.requiredArgs = nparamscheck_to_required_args(f->_nparamscheck); ft.pure = f->_purefunction; ft.nodiscard = f->_nodiscard; int nArgsFromMask = f->_typecheck.size() ? f->_typecheck.size()-1 : 0; // Exclude `this` int nArgsDisplayed = f->_nparamscheck >= 0 ? ft.requiredArgs : // for fixed args count ignore mask length (ft.requiredArgs > nArgsFromMask) ? ft.requiredArgs : nArgsFromMask; for (SQInteger n = 1; n <= nArgsDisplayed; n++) { char buf[16] = { 0 }; snprintf(buf, sizeof(buf), "arg%d", int(n)); ft.argNames.push_back(SQObjectPtr(SQString::Create(_ss(v), buf, -1))); ft.argTypeMask.push_back(n < f->_typecheck.size() ? f->_typecheck[n] : ~0u); ft.defaultValues.push_back(nullVal); // TODO: defaultValues } if (f->_nparamscheck < 0) { ft.ellipsisArgTypeMask = ~0u; } sq_pushobject(v, sq_stringify_function_type(v, ft)); return 1; } break; default: break; } sq_pushnull(v); return 1; } static SQInteger format_call_stack_string(HSQUIRRELVM v) { SQRESULT r = sqstd_formatcallstackstring(v); return SQ_SUCCEEDED(r) ? 1 : SQ_ERROR; } static SQInteger debug_type_mask_to_string(HSQUIRRELVM v) { SQInteger typeMask = 0; sq_getinteger(v, 2, &typeMask); char buf[2048]; sq_stringify_type_mask(buf, sizeof(buf), typeMask); sq_pushstring(v, buf, -1); return 1; } static SQUnsignedInteger32 sq_debug_time_msec() { return SQUnsignedInteger32(std::chrono::duration_cast(std::chrono::steady_clock::now().time_since_epoch()).count()); } static bool default_quirrel_watchdog_hook(HSQUIRRELVM v, bool kick) { if (_ss(v)->watchdog_threshold_msec && !kick) { SQUnsignedInteger32 curTime = sq_debug_time_msec(); if (curTime - _ss(v)->watchdog_last_alive_time_msec > _ss(v)->watchdog_threshold_msec) { _ss(v)->watchdog_last_alive_time_msec = curTime; return false; } return true; } if (kick) { _ss(v)->watchdog_last_alive_time_msec = sq_debug_time_msec(); return true; } return true; } SQInteger debug_script_watchdog_kick(HSQUIRRELVM v) { sq_kick_watchdog(v); return SQ_OK; } SQInteger debug_set_script_watchdog_timeout_msec(HSQUIRRELVM v) { SQInteger timeoutMsec = 0; sq_getinteger(v, 2, &timeoutMsec); SQInteger prevTimeout = sq_set_watchdog_timeout_msec(v, timeoutMsec); sq_pushinteger(v, prevTimeout); return 1; } static const SQRegFunctionFromStr debuglib_funcs[] = { { debug_seterrorhandler, "seterrorhandler(handler: function|null)", "Installs the given function as the VM error handler; null clears it" }, { debug_setdebughook, "setdebughook(hook: function|null)", "Installs the given function as the VM debug hook; null clears it" }, { debug_getstackinfos, "getstackinfos(level: int): table|null", "Returns call stack information for the given stack level" }, { format_call_stack_string, "format_call_stack_string(): string", "Returns a formatted string describing the current call stack" }, { debug_getlocals, "getlocals([level: int, include_internal: bool]): table", "Returns a table of local variables at the given stack level" }, { debug_get_stack_top, "get_stack_top(): int", "Returns the current VM stack top index" }, { debug_script_watchdog_kick, "script_watchdog_kick()", "Resets the script watchdog timer" }, { debug_set_script_watchdog_timeout_msec, "set_script_watchdog_timeout_msec(timeout_msec: int): int", "Sets the script watchdog timeout in milliseconds and returns the previous value" }, { debug_get_function_decl_string, "get_function_decl_string(func: function): string|null", "Returns a function declaration string" }, { debug_type_mask_to_string, "type_mask_to_string(mask: int): string", "Convert type mask to human-readable string" }, { debug_get_function_info_table, "get_function_info_table(func: function): table|null", "Returns meta information about a function as table" }, { debug_doc, "doc(subject: table|function|instance|class): string|null", "Returns a documentation string for a function, class, or table" }, #ifndef NO_GARBAGE_COLLECTOR { debug_collectgarbage, "collectgarbage(): int", "Runs the garbage collector and returns the number of reclaimed objects" }, { debug_resurrectunreachable, "resurrectunreachable(): array|null", "Resurrects unreachable objects for inspection" }, #endif { debug_getbuildinfo, "getbuildinfo(): table", "Returns a table describing the Quirrel build (version, sizes, GC status)" }, { NULL, NULL, NULL } }; SQRESULT sqstd_register_debuglib(HSQUIRRELVM v) { SQInteger i = 0; while (debuglib_funcs[i].f) { sq_new_closure_slot_from_decl_string(v, debuglib_funcs[i].f, 0, debuglib_funcs[i].declstring, debuglib_funcs[i].docstring); i++; } SQWATCHDOGHOOK prevHook = sq_set_watchdog_hook(v, default_quirrel_watchdog_hook); if (prevHook != nullptr) sq_set_watchdog_hook(v, prevHook); return SQ_OK; } ================================================ FILE: sqstdlib/sqstdhash.cpp ================================================ /* see copyright notice in squirrel.h */ #include #include #include #include #include #include #include #include // FNV-1a hash parameters #define SQ_M_HASH_MULTIPLIER 1099511628211LU #define SQ_M_HASH_INIT 14695981039346656037LU static SQInteger math_recursive_hash_impl(HSQUIRRELVM vm, HSQOBJECT &obj, SQUnsignedInteger prev_hash, SQUnsignedInteger &out_hash, SQInteger depth) { if (depth <= 0) { out_hash = prev_hash; return SQ_OK; } SQObjectType objType = sq_type(obj); prev_hash = (objType ^ prev_hash) * SQ_M_HASH_MULTIPLIER; switch (objType) { case OT_NULL: case OT_BOOL: case OT_INTEGER: out_hash = (SQUnsignedInteger(_integer(obj)) ^ prev_hash) * SQ_M_HASH_MULTIPLIER; break; case OT_FLOAT: { #ifdef SQUSEDOUBLE uint64_t fbits = 0; memcpy(&fbits, &_float(obj), sizeof(SQFloat)); out_hash = (SQUnsignedInteger(fbits ^ (fbits >> 42u)) ^ prev_hash) * SQ_M_HASH_MULTIPLIER; #else uint32_t fbits = 0; memcpy(&fbits, &_float(obj), sizeof(SQFloat)); out_hash = (SQUnsignedInteger(fbits ^ (fbits >> 21u)) ^ prev_hash) * SQ_M_HASH_MULTIPLIER; #endif break; } case OT_STRING: { SQString *str = _string(obj); out_hash = (prev_hash ^ str->_len) * SQ_M_HASH_MULTIPLIER; out_hash = (out_hash ^ str->_hash) * SQ_M_HASH_MULTIPLIER; break; } case OT_ARRAY: { SQArray *arr = _array(obj); SQUnsignedInteger h = prev_hash; SQInteger arrSize = arr->Size(); for (SQInteger i = 0; i < arrSize; ++i) { SQUnsignedInteger resultHash = 0; if (SQ_FAILED(math_recursive_hash_impl(vm, arr->_values[i], h, resultHash, depth - 1))) return SQ_ERROR; h = resultHash; } out_hash = h; break; } case OT_TABLE: case OT_CLASS: case OT_INSTANCE: { out_hash = prev_hash; sq_pushobject(vm, obj); sq_pushnull(vm); while (SQ_SUCCEEDED(sq_next(vm, -2))) { SQUnsignedInteger resultHash = 0; HSQOBJECT key = stack_get(vm, -2); if (SQ_FAILED(math_recursive_hash_impl(vm, key, out_hash, resultHash, depth - 1))) return SQ_ERROR; out_hash = resultHash; HSQOBJECT val = stack_get(vm, -1); if (SQ_FAILED(math_recursive_hash_impl(vm, val, out_hash, resultHash, depth - 1))) return SQ_ERROR; out_hash = resultHash; sq_pop(vm, 2); } sq_pop(vm, 2); break; } default: out_hash = prev_hash; return sq_throwerror(vm, "unsupported type for hashing"); } out_hash ^= out_hash >> 15; return SQ_OK; } SQInteger sq_math_hash(HSQUIRRELVM v) { HSQOBJECT obj; sq_getstackobj(v, 2, &obj); SQUnsignedInteger resultHash = 0; if (SQ_FAILED(math_recursive_hash_impl(v, obj, SQ_M_HASH_INIT, resultHash, 1))) return SQ_ERROR; sq_pushinteger(v, SQInteger(resultHash) & (~SQUnsignedInteger(0) >> 1)); return 1; } SQInteger sq_math_deep_hash(HSQUIRRELVM v) { SQInteger argDepth = 200; // not a very deep by default to avoid stack overflows in case of circular references if (sq_gettop(v) >= 3) { if (SQ_FAILED(sq_getinteger(v, 3, &argDepth))) return sq_throwerror(v, "invalid param"); if (argDepth < 1) return sq_throwerror(v, "hashing depth must be >= 1"); } HSQOBJECT obj; sq_getstackobj(v, 2, &obj); SQUnsignedInteger resultHash = 0; if (SQ_FAILED(math_recursive_hash_impl(v, obj, SQ_M_HASH_INIT, resultHash, argDepth))) return SQ_ERROR; sq_pushinteger(v, SQInteger(resultHash) & (~SQUnsignedInteger(0) >> 1)); return 1; } ================================================ FILE: sqstdlib/sqstdhash.h ================================================ #pragma once #include SQInteger sq_math_hash(HSQUIRRELVM v); SQInteger sq_math_deep_hash(HSQUIRRELVM v); ================================================ FILE: sqstdlib/sqstdio.cpp ================================================ /* see copyright notice in squirrel.h */ #include #include #include #include #include #include "sqstdstream.h" #define SQSTD_FILE_TYPE_TAG ((SQUnsignedInteger)(SQSTD_STREAM_TYPE_TAG | 0x00000001)) //basic API SQFILE sqstd_fopen(const char *filename ,const char *mode) { return (SQFILE)fopen(filename,mode); } SQInteger sqstd_fread(void* buffer, SQInteger size, SQInteger count, SQFILE file) { SQInteger ret = (SQInteger)fread(buffer,size,count,(FILE *)file); return ret; } SQInteger sqstd_fwrite(const SQUserPointer buffer, SQInteger size, SQInteger count, SQFILE file) { return (SQInteger)fwrite(buffer,size,count,(FILE *)file); } SQInteger sqstd_fseek(SQFILE file, SQInteger offset, SQInteger origin) { SQInteger realorigin; switch(origin) { case SQ_SEEK_CUR: realorigin = SEEK_CUR; break; case SQ_SEEK_END: realorigin = SEEK_END; break; case SQ_SEEK_SET: realorigin = SEEK_SET; break; default: return -1; //failed } return fseek((FILE *)file,(long)offset,(int)realorigin); } SQInteger sqstd_ftell(SQFILE file) { return ftell((FILE *)file); } SQInteger sqstd_fflush(SQFILE file) { return fflush((FILE *)file); } SQInteger sqstd_fclose(SQFILE file) { return fclose((FILE *)file); } SQInteger sqstd_feof(SQFILE file) { return feof((FILE *)file); } //File struct SQFile : public SQStream { SQFile(SQAllocContext alloc_ctx) : _alloc_ctx(alloc_ctx) , _handle(nullptr) , _owns(false) {} SQFile(SQAllocContext alloc_ctx, SQFILE file, bool owns) : _alloc_ctx(alloc_ctx) , _handle(file) , _owns(owns) {} virtual ~SQFile() { Close(); } bool Open(const char *filename ,const char *mode) { Close(); if( (_handle = sqstd_fopen(filename,mode)) ) { _owns = true; return true; } return false; } void Close() { if(_handle && _owns) { sqstd_fclose(_handle); _handle = NULL; _owns = false; } } SQInteger Read(void *buffer,SQInteger size) override { return sqstd_fread(buffer,1,size,_handle); } SQInteger Write(const void *buffer,SQInteger size) override { return sqstd_fwrite(const_cast(buffer), 1, size, _handle); } SQInteger Flush() override { return sqstd_fflush(_handle); } SQInteger Tell() override { return sqstd_ftell(_handle); } SQInteger Len() override { SQInteger prevpos=Tell(); Seek(0,SQ_SEEK_END); SQInteger size=Tell(); Seek(prevpos,SQ_SEEK_SET); return size; } SQInteger Seek(SQInteger offset, SQInteger origin) override { return sqstd_fseek(_handle,offset,origin); } bool IsValid() override { return _handle?true:false; } bool EOS() override { return Tell()==Len()?true:false;} SQFILE GetHandle() {return _handle;} public: SQAllocContext _alloc_ctx; private: SQFILE _handle; bool _owns; }; static SQInteger _file__typeof(HSQUIRRELVM v) { sq_pushstring(v,"file",-1); return 1; } static SQInteger _file_releasehook(HSQUIRRELVM SQ_UNUSED_ARG(vm), SQUserPointer p, SQInteger SQ_UNUSED_ARG(size)) { SQFile *self = (SQFile*)p; SQAllocContext alloc_ctx = self->_alloc_ctx; self->~SQFile(); sq_free(alloc_ctx, self,sizeof(SQFile)); return 1; } static bool _file_is_valid_mode(const char *mode) { static const char *ok[] = { "r", "w", "a", "r+", "w+", "a+", "rb", "wb", "ab", "rt", "wt", "at", "r+b", "w+b", "a+b", "rb+", "wb+", "ab+", "r+t", "w+t", "a+t", "rt+", "wt+", "at+", nullptr }; if (!mode) return false; for (int i = 0; ok[i]; ++i) if (!strcmp(mode, ok[i])) return true; return false; } static SQInteger _file_constructor(HSQUIRRELVM v) { const char *filename,*mode; bool owns = true; SQFile *f; SQFILE newf; if(sq_gettype(v,2) == OT_STRING && sq_gettype(v,3) == OT_STRING) { sq_getstring(v, 2, &filename); sq_getstring(v, 3, &mode); if(!_file_is_valid_mode(mode)) return sq_throwerror(v, "invalid file mode"); newf = sqstd_fopen(filename, mode); if(!newf) return sq_throwerror(v, "cannot open file"); } else if(sq_gettype(v,2) == OT_USERPOINTER) { owns = !(sq_gettype(v,3) == OT_NULL); sq_getuserpointer(v,2,&newf); } else { return sq_throwerror(v,"wrong parameter"); } SQAllocContext alloc_ctx = sq_getallocctx(v); f = new (sq_malloc(alloc_ctx, sizeof(SQFile)))SQFile(alloc_ctx,newf,owns); if(SQ_FAILED(sq_setinstanceup(v,1,f))) { f->~SQFile(); sq_free(alloc_ctx,f,sizeof(SQFile)); return sq_throwerror(v, "cannot create blob with negative size"); } sq_setreleasehook(v,1,_file_releasehook); return 0; } static SQInteger _file_close(HSQUIRRELVM v) { SQFile *self = NULL; if(SQ_SUCCEEDED(sq_getinstanceup(v,1,(SQUserPointer*)&self,(SQUserPointer)SQSTD_FILE_TYPE_TAG)) && self != NULL) { self->Close(); } return 0; } static const SQRegFunctionFromStr _file_methods[] = { { _file_constructor, "constructor(path: string|userpointer, mode: string|int|null): instance", "Two forms: (path: string, mode: string) opens the file via fopen with the given mode; " "(handle: userpointer, own) wraps an existing FILE* and takes ownership (closes on destruction) " "when `own` is non-null, or shares it without closing when `own` is null" }, { _file__typeof, "instance._typeof(): string", "Returns 'file'" }, { _file_close, "instance.close()", "Closes the file if it is still open" }, { NULL, NULL, NULL } }; SQRESULT sqstd_createfile(HSQUIRRELVM v, SQFILE file,SQBool own) { SQInteger top = sq_gettop(v); sq_pushregistrytable(v); sq_pushstring(v,"std_file",-1); if(SQ_SUCCEEDED(sq_get(v,-2))) { sq_remove(v,-2); //removes the registry sq_pushroottable(v); // push the this sq_pushuserpointer(v,file); //file if(own){ sq_pushinteger(v,1); //true } else{ sq_pushnull(v); //false } if(SQ_SUCCEEDED( sq_call(v,3,SQTrue,SQFalse) )) { sq_remove(v,-2); return SQ_OK; } } else assert(0); sq_settop(v,top); return SQ_ERROR; } SQRESULT sqstd_getfile(HSQUIRRELVM v, SQInteger idx, SQFILE *file) { SQFile *fileobj = NULL; if(SQ_SUCCEEDED(sq_getinstanceup(v,idx,(SQUserPointer*)&fileobj,(SQUserPointer)SQSTD_FILE_TYPE_TAG))) { *file = fileobj->GetHandle(); return SQ_OK; } return sq_throwerror(v,"not a file"); } #define IO_BUFFER_SIZE 2048 struct IOBuffer { unsigned char buffer[IO_BUFFER_SIZE]; SQInteger size; SQInteger ptr; SQFILE file; }; SQInteger _read_byte(IOBuffer *iobuffer) { if(iobuffer->ptr < iobuffer->size) { SQInteger ret = iobuffer->buffer[iobuffer->ptr]; iobuffer->ptr++; return ret; } else { if( (iobuffer->size = sqstd_fread(iobuffer->buffer,1,IO_BUFFER_SIZE,iobuffer->file )) > 0 ) { SQInteger ret = iobuffer->buffer[0]; iobuffer->ptr = 1; return ret; } } return 0; } SQInteger _read_two_bytes(IOBuffer *iobuffer) { if(iobuffer->ptr < iobuffer->size) { if(iobuffer->size < 2) return 0; SQInteger ret = *((const wchar_t*)&iobuffer->buffer[iobuffer->ptr]); iobuffer->ptr += 2; return ret; } else { if( (iobuffer->size = sqstd_fread(iobuffer->buffer,1,IO_BUFFER_SIZE,iobuffer->file )) > 0 ) { if(iobuffer->size < 2) return 0; SQInteger ret = *((const wchar_t*)&iobuffer->buffer[0]); iobuffer->ptr = 2; return ret; } } return 0; } SQInteger file_read(SQUserPointer file,SQUserPointer buf,SQInteger size) { SQInteger ret; if( ( ret = sqstd_fread(buf,1,size,(SQFILE)file ))!=0 )return ret; return -1; } SQInteger file_write(SQUserPointer file,SQUserPointer p,SQInteger size) { return sqstd_fwrite(p,1,size,(SQFILE)file); } static char *readFileData(HSQUIRRELVM v, SQFILE f, SQInteger &s) { sqstd_fseek(f, 0, SQ_SEEK_SET); SQInteger size = sqstd_fseek(f, 0, SQ_SEEK_END); char *buffer = (char *)sq_malloc(sq_getallocctx(v), size * sizeof(char)); sqstd_fseek(f, 0, SQ_SEEK_SET); s = sqstd_fread(buffer, sizeof(char), size, f); return buffer; } SQRESULT sqstd_loadfile(HSQUIRRELVM v,const char *filename,SQBool printerror) { SQFILE file = sqstd_fopen(filename,"rb"); if(file){ SQInteger size = 0; char *buffer = readFileData(v, file, size); SQRESULT r = SQ_ERROR; if(SQ_SUCCEEDED(sq_compile(v,buffer,size,filename,printerror))){ r = SQ_OK; } sq_free(sq_getallocctx(v), buffer, size); sqstd_fclose(file); return r; } return sq_throwerror(v,"cannot open the file"); } SQRESULT sqstd_dofile(HSQUIRRELVM v,const char *filename,SQBool retval,SQBool printerror) { //at least one entry must exist in order for us to push it as the environment if(sq_gettop(v) == 0) return sq_throwerror(v,"environment table expected"); if(SQ_SUCCEEDED(sqstd_loadfile(v,filename,printerror))) { sq_push(v,-2); if(SQ_SUCCEEDED(sq_call(v,1,retval,SQTrue))) { sq_remove(v,retval?-2:-1); //removes the closure return 1; } sq_pop(v,1); //removes the closure } return SQ_ERROR; } SQRESULT sqstd_writeclosuretofile(HSQUIRRELVM v,const char *filename) { SQFILE file = sqstd_fopen(filename,"wb+"); if(!file) return sq_throwerror(v,"cannot open the file"); if(SQ_SUCCEEDED(sq_writeclosure(v,file_write,file))) { sqstd_fclose(file); return SQ_OK; } sqstd_fclose(file); return SQ_ERROR; //forward the error } static const SQRegFunctionFromStr iolib_funcs[] = { { NULL, NULL, NULL } }; SQRESULT sqstd_register_iolib(HSQUIRRELVM v) { SQInteger top = sq_gettop(v); //create delegate declare_stream(v,"file",(SQUserPointer)SQSTD_FILE_TYPE_TAG,"std_file",_file_methods,iolib_funcs); sq_pushstring(v,"stdout",-1); sqstd_createfile(v,stdout,SQFalse); sq_newslot(v,-3,SQFalse); sq_pushstring(v,"stdin",-1); sqstd_createfile(v,stdin,SQFalse); sq_newslot(v,-3,SQFalse); sq_pushstring(v,"stderr",-1); sqstd_createfile(v,stderr,SQFalse); sq_newslot(v,-3,SQFalse); sq_settop(v,top); return SQ_OK; } ================================================ FILE: sqstdlib/sqstdmath.cpp ================================================ /* see copyright notice in squirrel.h */ #include #include #include #include #include #include #include #include #include #include "sqstdhash.h" #define SINGLE_ARG_FUNC(_funcname) static SQInteger math_##_funcname(HSQUIRRELVM v){ \ SQFloat f; \ sq_getfloat(v,2,&f); \ sq_pushfloat(v,(SQFloat)_funcname(f)); \ return 1; \ } #define TWO_ARGS_FUNC(_funcname) static SQInteger math_##_funcname(HSQUIRRELVM v){ \ SQFloat p1,p2; \ sq_getfloat(v,2,&p1); \ sq_getfloat(v,3,&p2); \ sq_pushfloat(v,(SQFloat)_funcname(p1,p2)); \ return 1; \ } #define SQ_MAX_INT_RANDOM 0x7FFFFFFF static void math_randomize_seed(HSQUIRRELVM v) { SQUnsignedInteger32 s = SQUnsignedInteger32(size_t(v->_sharedstate)) * 123 + SQUnsignedInteger32(size_t(&v)); s ^= SQUnsignedInteger32(std::chrono::high_resolution_clock::now().time_since_epoch().count()); v->_sharedstate->rand_seed += s ^ (s >> 10); } static SQInteger math_srand(HSQUIRRELVM v) { SQInteger i; if(SQ_FAILED(sq_getinteger(v,2,&i))) return sq_throwerror(v,"invalid param"); v->_sharedstate->rand_seed = SQUnsignedInteger32(i); return 0; } // Constants 1664525, 1013904223 are based on the multiplier and increment suggested by Numerical Recipes // and used by the PCG family of random number generators. // This LCG has a full period of 2^32 and a good spectral test value of M_8 = 0.73201 static SQInteger math_rand(HSQUIRRELVM v) { SQUnsignedInteger32 seed = v->_sharedstate->rand_seed = 1664525 * v->_sharedstate->rand_seed + 1013904223; sq_pushinteger(v, (seed ^ (seed >> 16u)) & SQ_MAX_INT_RANDOM); return 1; } static SQInteger math_abs(HSQUIRRELVM v) { SQObject arg; sq_getstackobj(v, 2, &arg); if (sq_type(arg) == OT_FLOAT) sq_pushfloat(v, fabs(_float(arg))); else sq_pushinteger(v, abs(_integer(arg))); return 1; } static SQInteger math_acos(HSQUIRRELVM v) { SQFloat f; sq_getfloat(v, 2, &f); if (f < SQFloat(-1)) f = SQFloat(-1); if (f > SQFloat(1)) f = SQFloat(1); sq_pushfloat(v, (SQFloat)acos(f)); return 1; } static SQInteger math_asin(HSQUIRRELVM v) { SQFloat f; sq_getfloat(v, 2, &f); if (f < SQFloat(-1)) f = SQFloat(-1); if (f > SQFloat(1)) f = SQFloat(1); sq_pushfloat(v, (SQFloat)asin(f)); return 1; } SINGLE_ARG_FUNC(sqrt) SINGLE_ARG_FUNC(fabs) SINGLE_ARG_FUNC(sin) SINGLE_ARG_FUNC(cos) SINGLE_ARG_FUNC(log) SINGLE_ARG_FUNC(log10) SINGLE_ARG_FUNC(tan) SINGLE_ARG_FUNC(atan) TWO_ARGS_FUNC(atan2) TWO_ARGS_FUNC(pow) SINGLE_ARG_FUNC(floor) SINGLE_ARG_FUNC(ceil) SINGLE_ARG_FUNC(round) SINGLE_ARG_FUNC(exp) template static SQInteger math_min_max(HSQUIRRELVM v) { SQInteger nArgs = sq_gettop(v); SQObject objRes; sq_getstackobj(v, 2, &objRes); for (SQInteger i = 3; i <= nArgs; ++i) { SQObject cur; sq_getstackobj(v, i, &cur); if (!(sq_type(cur) & SQOBJECT_NUMERIC)) { sq_throwparamtypeerror(v, i, _RT_FLOAT | _RT_INTEGER, sq_type(cur)); return SQ_ERROR; } SQInteger cres = 0; if (!sq_cmpraw(v, cur, objRes, cres)) return sq_throwerror(v, "Internal error, comparison failed"); if (cres == CmpRes) objRes = cur; } sq_pushobject(v, objRes); return 1; } static SQInteger math_min(HSQUIRRELVM v) { return math_min_max<-1>(v); } static SQInteger math_max(HSQUIRRELVM v) { return math_min_max<+1>(v); } static SQInteger math_clamp(HSQUIRRELVM v) { SQObject x, lo, hi; SQInteger cres = 0; sq_getstackobj(v, 2, &x); sq_getstackobj(v, 3, &lo); sq_getstackobj(v, 4, &hi); const char *cmpFailedErrText = "Internal error, comparison failed"; if (!sq_cmpraw(v, lo, hi, cres)) return sq_throwerror(v, cmpFailedErrText); if (cres > 0) return sq_throwerror(v, "Invalid clamp range: min>max"); if (!sq_cmpraw(v, x, lo, cres)) return sq_throwerror(v, cmpFailedErrText); if (cres < 0) { sq_pushobject(v, lo); return 1; } if (!sq_cmpraw(v, x, hi, cres)) return sq_throwerror(v, cmpFailedErrText); if (cres > 0) { sq_pushobject(v, hi); return 1; } sq_pushobject(v, x); return 1; } #ifndef M_PI #define M_PI (3.14159265358979323846) #endif static const SQRegFunctionFromStr mathlib_funcs[] = { { math_sqrt, "pure sqrt(x: number): number", "Returns the square root of x" }, { math_sin, "pure sin(x: number): number", "Returns the sine of x (in radians)" }, { math_cos, "pure cos(x: number): number", "Returns the cosine of x (in radians)" }, { math_asin, "pure asin(x: number): number", "Returns the arcsine of x" }, { math_acos, "pure acos(x: number): number", "Returns the arccosine of x" }, { math_log, "pure log(x: number): number", "Returns the natural logarithm of x" }, { math_log10, "pure log10(x: number): number", "Returns the base-10 logarithm of x" }, { math_tan, "pure tan(x: number): number", "Returns the tangent of x (in radians)" }, { math_atan, "pure atan(x: number): number", "Returns the arctangent of x" }, { math_atan2, "pure atan2(y: number, x: number): number", "Returns the arctangent of y/x using the signs to determine the quadrant" }, { math_pow, "pure pow(x: number, y: number): number", "Returns x raised to the power y" }, { math_floor, "pure floor(x: number): number", "Rounds x downward to the nearest integer" }, { math_ceil, "pure ceil(x: number): number", "Rounds x upward to the nearest integer" }, { math_round, "pure round(x: number): number", "Rounds x to the nearest integer" }, { math_exp, "pure exp(x: number): number", "Returns e raised to the power of x" }, { math_srand, "srand(seed: int)", "Sets the seed for rand()" }, { math_rand, "rand(): int", "Returns a random integer in range 0..RAND_MAX" }, { math_fabs, "pure fabs(x: number): float", "Returns the absolute value of float(x)" }, { math_abs, "pure abs(x: number): number", "Returns the absolute value of x" }, { math_min, "pure min(x: number, y: number, ...: number): number", "Returns the minimum value from the arguments" }, { math_max, "pure max(x: number, y: number, ...: number): number", "Returns the maximum value from the arguments" }, { math_clamp, "pure clamp(x: number, min: number, max: number): number", "Clamps x to the range [min, max]" }, { sq_math_hash, "pure hash(obj): int", "Returns a hash value for the given object" }, { sq_math_deep_hash, "pure deep_hash(obj, [depth: int]): int", "Returns a hash value for the given object, hashing nested objects up to the specified depth" }, { NULL, NULL, NULL } }; SQRESULT sqstd_register_mathlib(HSQUIRRELVM v) { math_randomize_seed(v); SQInteger i = 0; while (mathlib_funcs[i].f) { sq_new_closure_slot_from_decl_string(v, mathlib_funcs[i].f, 0, mathlib_funcs[i].declstring, mathlib_funcs[i].docstring); i++; } // Constants sq_pushstring(v,"RAND_MAX",-1); sq_pushinteger(v,SQ_MAX_INT_RANDOM); sq_newslot(v,-3,SQFalse); sq_pushstring(v,"PI",-1); sq_pushfloat(v,(SQFloat)M_PI); sq_newslot(v,-3,SQFalse); sq_pushstring(v,"FLT_MAX",-1); sq_pushfloat(v,(SQFloat)FLT_MAX); sq_newslot(v,-3,SQFalse); sq_pushstring(v,"FLT_MIN",-1); sq_pushfloat(v,(SQFloat)FLT_MIN); sq_newslot(v,-3,SQFalse); return SQ_OK; } ================================================ FILE: sqstdlib/sqstdrex.cpp ================================================ /* see copyright notice in squirrel.h */ #include #include #include #include #include #include #define SQREX_DEBUG 0 #if SQREX_DEBUG #include static const char *g_nnames[] = { "NONE","OP_GREEDY", "OP_OR", "OP_EXPR","OP_NOCAPEXPR","OP_DOT", "OP_CLASS", "OP_CCLASS","OP_NCLASS","OP_RANGE","OP_CHAR", "OP_EOL","OP_BOL","OP_WB","OP_MB" }; #endif #define SQ_MAX_CHAR 0xFF #define OP_GREEDY (SQ_MAX_CHAR+1) // * + ? {n} #define OP_OR (SQ_MAX_CHAR+2) #define OP_EXPR (SQ_MAX_CHAR+3) //parentesis () #define OP_NOCAPEXPR (SQ_MAX_CHAR+4) //parentesis (?:) #define OP_DOT (SQ_MAX_CHAR+5) #define OP_CLASS (SQ_MAX_CHAR+6) #define OP_CCLASS (SQ_MAX_CHAR+7) #define OP_NCLASS (SQ_MAX_CHAR+8) //negates class the [^ #define OP_RANGE (SQ_MAX_CHAR+9) //#define OP_CHAR (SQ_MAX_CHAR+10) #define OP_EOL (SQ_MAX_CHAR+11) #define OP_BOL (SQ_MAX_CHAR+12) #define OP_WB (SQ_MAX_CHAR+13) #define OP_MB (SQ_MAX_CHAR+14) //match balanced #define SQREX_SYMBOL_ANY_CHAR ('.') #define SQREX_SYMBOL_GREEDY_ONE_OR_MORE ('+') #define SQREX_SYMBOL_GREEDY_ZERO_OR_MORE ('*') #define SQREX_SYMBOL_GREEDY_ZERO_OR_ONE ('?') #define SQREX_SYMBOL_BRANCH ('|') #define SQREX_SYMBOL_END_OF_STRING ('$') #define SQREX_SYMBOL_BEGINNING_OF_STRING ('^') #define SQREX_SYMBOL_ESCAPE_CHAR ('\\') typedef int SQRexNodeType; typedef struct tagSQRexNode{ SQRexNodeType type; SQInteger left; SQInteger right; SQInteger next; }SQRexNode; struct SQRex{ SQAllocContext _alloc_ctx; const char *_eol; const char *_bol; const char *_p; SQInteger _first; SQRexNode *_nodes; SQInteger _nallocated; SQInteger _nsize; SQInteger _depth; SQInteger _nsubexpr; SQRexMatch *_matches; SQInteger _currsubexp; void *_jmpbuf; const char **_error; }; static SQInteger sqstd_rex_list(SQRex *exp); static SQInteger sqstd_rex_newnode(SQRex *exp, SQRexNodeType type) { SQRexNode n; n.type = type; n.next = n.right = n.left = -1; if(type == OP_EXPR) n.right = exp->_nsubexpr++; if(exp->_nallocated < (exp->_nsize + 1)) { SQInteger oldsize = exp->_nallocated; exp->_nallocated *= 2; exp->_nodes = (SQRexNode *)sq_realloc(exp->_alloc_ctx,exp->_nodes, oldsize * sizeof(SQRexNode) ,exp->_nallocated * sizeof(SQRexNode)); } exp->_nodes[exp->_nsize++] = n; SQInteger newid = exp->_nsize - 1; return (SQInteger)newid; } static void sqstd_rex_error(SQRex *exp,const char *error) { if(exp->_error) *exp->_error = error; longjmp(*((jmp_buf*)exp->_jmpbuf),-1); } static void sqstd_rex_expect(SQRex *exp, SQInteger n){ if((*exp->_p) != n) sqstd_rex_error(exp, "expected paren"); exp->_p++; } static char sqstd_rex_escapechar(SQRex *exp) { if(*exp->_p == SQREX_SYMBOL_ESCAPE_CHAR){ exp->_p++; if(*exp->_p == '\0') sqstd_rex_error(exp,"letter expected"); switch(*exp->_p) { case 'v': exp->_p++; return '\v'; case 'n': exp->_p++; return '\n'; case 't': exp->_p++; return '\t'; case 'r': exp->_p++; return '\r'; case 'f': exp->_p++; return '\f'; default: return (*exp->_p++); } } else if(!sq_isprint(*exp->_p)) sqstd_rex_error(exp,"letter expected"); return (*exp->_p++); } static SQInteger sqstd_rex_charclass(SQRex *exp,SQInteger classid) { SQInteger n = sqstd_rex_newnode(exp,OP_CCLASS); exp->_nodes[n].left = classid; return n; } static SQInteger sqstd_rex_charnode(SQRex *exp,SQBool isclass) { char t; if(*exp->_p == SQREX_SYMBOL_ESCAPE_CHAR) { exp->_p++; switch(*exp->_p) { case 'n': exp->_p++; return sqstd_rex_newnode(exp,'\n'); case 't': exp->_p++; return sqstd_rex_newnode(exp,'\t'); case 'r': exp->_p++; return sqstd_rex_newnode(exp,'\r'); case 'f': exp->_p++; return sqstd_rex_newnode(exp,'\f'); case 'v': exp->_p++; return sqstd_rex_newnode(exp,'\v'); case 'a': case 'A': case 'w': case 'W': case 's': case 'S': case 'd': case 'D': case 'x': case 'X': case 'c': case 'C': case 'p': case 'P': case 'l': case 'u': { t = *exp->_p; exp->_p++; return sqstd_rex_charclass(exp,t); } case 'm': { exp->_p++; //skip 'm' if(*exp->_p == '\0') sqstd_rex_error(exp,"balanced chars expected"); char cb = *exp->_p; //cb = character begin match exp->_p++; if(*exp->_p == '\0') sqstd_rex_error(exp,"balanced chars expected"); char ce = *exp->_p; //ce = character end match exp->_p++; //points to the next char to be parsed if ( cb == ce ) sqstd_rex_error(exp,"open/close char can't be the same"); SQInteger node = sqstd_rex_newnode(exp,OP_MB); exp->_nodes[node].left = cb; exp->_nodes[node].right = ce; return node; } case 0: sqstd_rex_error(exp,"letter expected for argument of escape sequence"); break; case 'b': case 'B': if(!isclass) { SQInteger node = sqstd_rex_newnode(exp,OP_WB); exp->_nodes[node].left = *exp->_p; exp->_p++; return node; } [[fallthrough]]; default: t = *exp->_p; exp->_p++; return sqstd_rex_newnode(exp,t); } } else if(!sq_isprint(*exp->_p)) { sqstd_rex_error(exp,"letter expected"); } t = *exp->_p; exp->_p++; return sqstd_rex_newnode(exp,t); } static SQInteger sqstd_rex_class(SQRex *exp) { SQInteger ret = -1; SQInteger first = -1,chain; if(*exp->_p == SQREX_SYMBOL_BEGINNING_OF_STRING){ ret = sqstd_rex_newnode(exp,OP_NCLASS); exp->_p++; }else ret = sqstd_rex_newnode(exp,OP_CLASS); if(*exp->_p == ']') sqstd_rex_error(exp,"empty class"); chain = ret; while(*exp->_p != ']' && *exp->_p != '\0') { if(*exp->_p == '-' && first != -1){ SQInteger r; exp->_p++; // skip '-' if(*exp->_p == ']') sqstd_rex_error(exp,"unfinished range"); r = sqstd_rex_newnode(exp,OP_RANGE); if(exp->_nodes[first].type == OP_CCLASS) sqstd_rex_error(exp,"cannot use character classes in ranges"); exp->_nodes[r].left = exp->_nodes[first].type; SQInteger t = sqstd_rex_escapechar(exp); if(exp->_nodes[r].left > t) sqstd_rex_error(exp,"invalid range"); exp->_nodes[r].right = t; exp->_nodes[chain].next = r; chain = r; first = -1; } else{ if(first!=-1){ SQInteger c = first; exp->_nodes[chain].next = c; chain = c; first = sqstd_rex_charnode(exp,SQTrue); } else{ first = sqstd_rex_charnode(exp,SQTrue); } } } if(*exp->_p == '\0') sqstd_rex_error(exp,"unterminated character class"); if(first!=-1){ SQInteger c = first; exp->_nodes[chain].next = c; } /* hack? */ exp->_nodes[ret].left = exp->_nodes[ret].next; exp->_nodes[ret].next = -1; return ret; } static SQInteger sqstd_rex_parsenumber(SQRex *exp) { SQInteger ret = *exp->_p-'0'; SQInteger positions = 10; exp->_p++; while(sq_isdigit(*exp->_p)) { ret = ret*10+(*exp->_p++-'0'); if(positions==1000000000) sqstd_rex_error(exp,"overflow in numeric constant"); positions *= 10; }; return ret; } static SQInteger sqstd_rex_element(SQRex *exp) { exp->_depth++; struct AutoDec { AutoDec(SQInteger *varPtr) : varPtr(varPtr) {} ~AutoDec() { (*varPtr)--; } SQInteger *varPtr; } autodec(&exp->_depth); if(exp->_depth > 200) sqstd_rex_error(exp, "pattern exceeds maximum allowed nesting depth"); if(exp->_nsize > 2000) sqstd_rex_error(exp, "pattern too complex"); SQInteger ret = -1; switch(*exp->_p) { case '(': { SQInteger expr; exp->_p++; if(*exp->_p =='?') { exp->_p++; sqstd_rex_expect(exp,':'); expr = sqstd_rex_newnode(exp,OP_NOCAPEXPR); } else expr = sqstd_rex_newnode(exp,OP_EXPR); SQInteger newn = sqstd_rex_list(exp); exp->_nodes[expr].left = newn; ret = expr; sqstd_rex_expect(exp,')'); } break; case '[': exp->_p++; ret = sqstd_rex_class(exp); sqstd_rex_expect(exp,']'); break; case SQREX_SYMBOL_END_OF_STRING: exp->_p++; ret = sqstd_rex_newnode(exp,OP_EOL);break; case SQREX_SYMBOL_ANY_CHAR: exp->_p++; ret = sqstd_rex_newnode(exp,OP_DOT);break; default: ret = sqstd_rex_charnode(exp,SQFalse); break; } SQBool isgreedy = SQFalse; unsigned short p0 = 0, p1 = 0; switch(*exp->_p){ case SQREX_SYMBOL_GREEDY_ZERO_OR_MORE: p0 = 0; p1 = 0xFFFF; exp->_p++; isgreedy = SQTrue; break; case SQREX_SYMBOL_GREEDY_ONE_OR_MORE: p0 = 1; p1 = 0xFFFF; exp->_p++; isgreedy = SQTrue; break; case SQREX_SYMBOL_GREEDY_ZERO_OR_ONE: p0 = 0; p1 = 1; exp->_p++; isgreedy = SQTrue; break; case '{': exp->_p++; { if(!sq_isdigit(*exp->_p)) sqstd_rex_error(exp,"number expected"); SQInteger n = sqstd_rex_parsenumber(exp); if(n > 0xFFFE) sqstd_rex_error(exp,"quantifier value too large"); p0 = (unsigned short)n; } /*******************************/ switch(*exp->_p) { case '}': p1 = p0; exp->_p++; break; case ',': exp->_p++; p1 = 0xFFFF; if(sq_isdigit(*exp->_p)){ SQInteger n = sqstd_rex_parsenumber(exp); if(n > 0xFFFE) sqstd_rex_error(exp,"quantifier value too large"); p1 = (unsigned short)n; } sqstd_rex_expect(exp,'}'); if(p0 > p1) sqstd_rex_error(exp,"invalid quantifier range: min > max"); break; default: sqstd_rex_error(exp,", or } expected"); } /*******************************/ isgreedy = SQTrue; break; } if(isgreedy) { SQInteger nnode = sqstd_rex_newnode(exp,OP_GREEDY); exp->_nodes[nnode].left = ret; exp->_nodes[nnode].right = ((SQInteger)p0<<16)|p1; ret = nnode; } if((*exp->_p != SQREX_SYMBOL_BRANCH) && (*exp->_p != ')') && (*exp->_p != SQREX_SYMBOL_GREEDY_ZERO_OR_MORE) && (*exp->_p != SQREX_SYMBOL_GREEDY_ONE_OR_MORE) && (*exp->_p != '\0')) { SQInteger nnode = sqstd_rex_element(exp); exp->_nodes[ret].next = nnode; } return ret; } static SQInteger sqstd_rex_list(SQRex *exp) { SQInteger ret=-1,e; if(*exp->_p == SQREX_SYMBOL_BEGINNING_OF_STRING) { exp->_p++; ret = sqstd_rex_newnode(exp,OP_BOL); } e = sqstd_rex_element(exp); if(ret != -1) { exp->_nodes[ret].next = e; } else ret = e; if(*exp->_p == SQREX_SYMBOL_BRANCH) { SQInteger temp,tright; exp->_p++; temp = sqstd_rex_newnode(exp,OP_OR); exp->_nodes[temp].left = ret; tright = sqstd_rex_list(exp); exp->_nodes[temp].right = tright; ret = temp; } return ret; } static SQBool sqstd_rex_matchcclass(SQInteger cclass,char c) { switch(cclass) { case 'a': return sq_isalpha(c)?SQTrue:SQFalse; case 'A': return !sq_isalpha(c)?SQTrue:SQFalse; case 'w': return (sq_isalnum(c) || c == '_')?SQTrue:SQFalse; case 'W': return (!sq_isalnum(c) && c != '_')?SQTrue:SQFalse; case 's': return sq_isspace(c)?SQTrue:SQFalse; case 'S': return !sq_isspace(c)?SQTrue:SQFalse; case 'd': return sq_isdigit(c)?SQTrue:SQFalse; case 'D': return !sq_isdigit(c)?SQTrue:SQFalse; case 'x': return sq_isxdigit(c)?SQTrue:SQFalse; case 'X': return !sq_isxdigit(c)?SQTrue:SQFalse; case 'c': return sq_iscntrl(c)?SQTrue:SQFalse; case 'C': return !sq_iscntrl(c)?SQTrue:SQFalse; case 'p': return sq_ispunct(c)?SQTrue:SQFalse; case 'P': return !sq_ispunct(c)?SQTrue:SQFalse; case 'l': return sq_islower(c)?SQTrue:SQFalse; case 'u': return sq_isupper(c)?SQTrue:SQFalse; } return SQFalse; /*cannot happen*/ } static SQBool sqstd_rex_matchclass(SQRex* exp,SQRexNode *node,char c) { do { switch(node->type) { case OP_RANGE: if((unsigned char)c >= node->left && (unsigned char)c <= node->right) return SQTrue; break; case OP_CCLASS: if(sqstd_rex_matchcclass(node->left,c)) return SQTrue; break; default: if(c == node->type)return SQTrue; } } while((node->next != -1) && (node = &exp->_nodes[node->next])); return SQFalse; } static const char *sqstd_rex_matchnode(SQRex* exp,SQRexNode *node,const char *str,SQRexNode *next) { SQRexNodeType type = node->type; switch(type) { case OP_GREEDY: { //SQRexNode *greedystop = (node->next != -1) ? &exp->_nodes[node->next] : NULL; SQRexNode *greedystop = NULL; SQInteger p0 = (node->right >> 16)&0x0000FFFF, p1 = node->right&0x0000FFFF, nmaches = 0; const char *s=str, *good = str; if(node->next != -1) { greedystop = &exp->_nodes[node->next]; } else { greedystop = next; } while((nmaches == 0xFFFF || nmaches < p1)) { const char *stop; const char *prev = s; if(!(s = sqstd_rex_matchnode(exp,&exp->_nodes[node->left],s,greedystop))) break; nmaches++; good=s; if(s == prev) break; // zero-width match, don't loop if(greedystop) { //checks that 0 matches satisfy the expression(if so skips) //if not would always stop(for instance if is a '?') if(greedystop->type != OP_GREEDY || ((greedystop->right >> 16)&0x0000FFFF) != 0) { SQRexNode *gnext = NULL; if(greedystop->next != -1) { gnext = &exp->_nodes[greedystop->next]; }else if(next && next->next != -1){ gnext = &exp->_nodes[next->next]; } stop = sqstd_rex_matchnode(exp,greedystop,s,gnext); if(stop) { //if satisfied stop it if(p0 == p1 && p0 == nmaches) break; else if(nmaches >= p0 && p1 == 0xFFFF) break; else if(nmaches >= p0 && nmaches <= p1) break; } } } if(s >= exp->_eol) break; } if(p0 == p1 && p0 == nmaches) return good; else if(nmaches >= p0 && p1 == 0xFFFF) return good; else if(nmaches >= p0 && nmaches <= p1) return good; return NULL; } case OP_OR: { const char *asd = str; SQRexNode *temp=&exp->_nodes[node->left]; while( (asd = sqstd_rex_matchnode(exp,temp,asd,NULL)) ) { if(temp->next != -1) temp = &exp->_nodes[temp->next]; else return asd; } asd = str; temp = &exp->_nodes[node->right]; while( (asd = sqstd_rex_matchnode(exp,temp,asd,NULL)) ) { if(temp->next != -1) temp = &exp->_nodes[temp->next]; else return asd; } return NULL; } case OP_EXPR: case OP_NOCAPEXPR:{ SQRexNode *n = &exp->_nodes[node->left]; const char *cur = str; SQInteger capture = -1; if(node->type != OP_NOCAPEXPR && node->right == exp->_currsubexp) { capture = exp->_currsubexp; exp->_matches[capture].begin = cur; exp->_currsubexp++; } SQInteger tempcap = exp->_currsubexp; do { SQRexNode *subnext = NULL; if(n->next != -1) { subnext = &exp->_nodes[n->next]; }else { subnext = next; } if(!(cur = sqstd_rex_matchnode(exp,n,cur,subnext))) { if(capture != -1){ exp->_matches[capture].begin = 0; exp->_matches[capture].len = 0; } return NULL; } } while((n->next != -1) && (n = &exp->_nodes[n->next])); exp->_currsubexp = tempcap; if(capture != -1) exp->_matches[capture].len = cur - exp->_matches[capture].begin; return cur; } case OP_WB: { SQBool isWordBoundary; if(str == exp->_bol) { isWordBoundary = (str < exp->_eol && !sq_isspace(*str)) ? SQTrue : SQFalse; } else if(str == exp->_eol) { isWordBoundary = !sq_isspace(*(str-1)) ? SQTrue : SQFalse; } else { isWordBoundary = (sq_isspace(*str) != sq_isspace(*(str-1))) ? SQTrue : SQFalse; } return (node->left == 'b') ? (isWordBoundary ? str : NULL) : (isWordBoundary ? NULL : str); } case OP_BOL: if(str == exp->_bol) return str; return NULL; case OP_EOL: if(str == exp->_eol) return str; return NULL; case OP_DOT:{ if (str == exp->_eol) return NULL; str++; } return str; case OP_NCLASS: case OP_CLASS: if (str == exp->_eol) return NULL; if(sqstd_rex_matchclass(exp,&exp->_nodes[node->left],*str)?(type == OP_CLASS?SQTrue:SQFalse):(type == OP_NCLASS?SQTrue:SQFalse)) { str++; return str; } return NULL; case OP_CCLASS: if (str == exp->_eol) return NULL; if(sqstd_rex_matchcclass(node->left,*str)) { str++; return str; } return NULL; case OP_MB: { SQInteger cb = node->left; //char that opens a balanced expression if(str == exp->_eol || *str != cb) return NULL; // string doesnt start with open char SQInteger ce = node->right; //char that closes a balanced expression SQInteger cont = 1; const char *streol = exp->_eol; while (++str < streol) { if (*str == ce) { if (--cont == 0) { return ++str; } } else if (*str == cb) cont++; } } return NULL; // string ends out of balance default: /* char */ if (str == exp->_eol) return NULL; if(*str != node->type) return NULL; str++; return str; } return NULL; } /* public api */ SQRex *sqstd_rex_compile(SQAllocContext alloc_ctx, const char *pattern,const char **error) { SQRex * volatile exp = (SQRex *)sq_malloc(alloc_ctx, sizeof(SQRex)); // "volatile" is needed for setjmp() exp->_alloc_ctx = alloc_ctx; exp->_eol = exp->_bol = NULL; exp->_p = pattern; exp->_nallocated = (SQInteger)strlen(pattern) * sizeof(char); if(exp->_nallocated < 4) exp->_nallocated = 4; exp->_nodes = (SQRexNode *)sq_malloc(alloc_ctx, exp->_nallocated * sizeof(SQRexNode)); exp->_nsize = 0; exp->_depth = 0; exp->_matches = 0; exp->_nsubexpr = 0; exp->_first = sqstd_rex_newnode(exp,OP_EXPR); exp->_error = error; exp->_jmpbuf = sq_malloc(alloc_ctx, sizeof(jmp_buf)); if(setjmp(*((jmp_buf*)exp->_jmpbuf)) == 0) { SQInteger res = sqstd_rex_list(exp); exp->_nodes[exp->_first].left = res; if(*exp->_p!='\0') sqstd_rex_error(exp,"unexpected character"); #if SQREX_DEBUG { SQInteger nsize,i; SQRexNode *t; nsize = exp->_nsize; t = &exp->_nodes[0]; printf("\n"); for(i = 0;i < nsize; i++) { if(exp->_nodes[i].type>SQ_MAX_CHAR) printf("[%02d] %10s ", (SQInt32)i,g_nnames[exp->_nodes[i].type-SQ_MAX_CHAR]); else printf("[%02d] %10c ", (SQInt32)i,exp->_nodes[i].type); printf("left %02d right %02d next %02d\n", (SQInt32)exp->_nodes[i].left, (SQInt32)exp->_nodes[i].right, (SQInt32)exp->_nodes[i].next); } printf("\n"); } #endif exp->_matches = (SQRexMatch *) sq_malloc(alloc_ctx, exp->_nsubexpr * sizeof(SQRexMatch)); memset(exp->_matches,0,exp->_nsubexpr * sizeof(SQRexMatch)); } else{ sqstd_rex_free(exp); return NULL; } return exp; } void sqstd_rex_free(SQRex *exp) { if(exp) { if(exp->_nodes) sq_free(exp->_alloc_ctx, exp->_nodes,exp->_nallocated * sizeof(SQRexNode)); if(exp->_jmpbuf) sq_free(exp->_alloc_ctx, exp->_jmpbuf,sizeof(jmp_buf)); if(exp->_matches) sq_free(exp->_alloc_ctx, exp->_matches,exp->_nsubexpr * sizeof(SQRexMatch)); sq_free(exp->_alloc_ctx, exp, sizeof(SQRex)); } } SQBool sqstd_rex_match(SQRex* exp,const char* text) { const char* res = NULL; exp->_bol = text; exp->_eol = text + strlen(text); exp->_currsubexp = 0; res = sqstd_rex_matchnode(exp,exp->_nodes,text,NULL); if(res == NULL || res != exp->_eol) return SQFalse; return SQTrue; } SQBool sqstd_rex_searchrange(SQRex* exp,const char* text_begin,const char* text_end,const char** out_begin, const char** out_end) { const char *cur = NULL; SQInteger node; if(text_begin >= text_end) return SQFalse; exp->_bol = text_begin; exp->_eol = text_end; do { node = exp->_first; cur = text_begin; while(node != -1) { exp->_currsubexp = 0; cur = sqstd_rex_matchnode(exp,&exp->_nodes[node],cur,NULL); if(!cur) break; node = exp->_nodes[node].next; } text_begin++; } while(cur == NULL && text_begin != text_end); if(cur == NULL) return SQFalse; --text_begin; if(out_begin) *out_begin = text_begin; if(out_end) *out_end = cur; return SQTrue; } SQBool sqstd_rex_search(SQRex* exp,const char* text, const char** out_begin, const char** out_end) { return sqstd_rex_searchrange(exp,text,text + strlen(text),out_begin,out_end); } SQInteger sqstd_rex_getsubexpcount(SQRex* exp) { return exp->_nsubexpr; } SQBool sqstd_rex_getsubexp(SQRex* exp, SQInteger n, SQRexMatch *subexp) { if( n<0 || n >= exp->_nsubexpr) return SQFalse; *subexp = exp->_matches[n]; return SQTrue; } ================================================ FILE: sqstdlib/sqstdserialization.cpp ================================================ /* see copyright notice in squirrel.h */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "sqstdstream.h" #include "sqstdblobimpl.h" #include #include #define STDLIB std #define SQ_DESER_MAX_OBJECT_SIZE (80 * 1024 * 1024) struct SQStreamSerializer { enum DataType { TP_NULL = 0 << 4, TP_BOOL = 1 << 4, TP_INTEGER = 2 << 4, TP_FLOAT = 3 << 4, TP_STRING = 4 << 4, TP_ARRAY = 5 << 4, TP_TABLE = 6 << 4, TP_INSTANCE = 7 << 4, }; enum { START_MARKER = 0xEA, END_MARKER = 0xFA, MAX_DEPTH = 200, }; struct ClassDesc { int index; // -1 if not serialized yet const char *className; }; HSQUIRRELVM vm; SQStream *stream; const char *errorString; STDLIB::unordered_map stringTable; // string -> index mapping STDLIB::vector stringList; // to keep track of strings STDLIB::unordered_map classTable; STDLIB::vector classList; // to keep track of classes SQObjectPtr availableClasses; // null or a table of available classes (class_name -> class) SQObjectPtr getstateProcName; SQObjectPtr setstateProcName; uint32_t stringCounter; uint32_t classCounter; uint32_t depth; SQStreamSerializer() : vm(nullptr), stream(nullptr), errorString(nullptr), stringCounter(0), depth(0), classCounter(0) {} int getCountOfParams(SQObjectPtr obj) { if (sq_isclosure(obj)) return _closure(obj)->_function->_nparameters; else if (sq_isnativeclosure(obj)) return _nativeclosure(obj)->_nparamscheck; else return -1; } bool fillAvailableClassNamesOnDemand() { if (!classTable.empty()) return true; if (sq_isnull(availableClasses)) return true; if (!sq_istable(availableClasses)) { errorString = "Available classes must be a table (class_name -> class)"; return false; } SQTable *tbl = _table(availableClasses); SQTable::_HashNode *node = tbl->_nodes; uint32_t count = tbl->_numofnodes_minus_one + 1; for (uint32_t i = 0; i < count; ++i) { SQObjectPtr key = node[i].key; if (!sq_isstring(key)) continue; SQObjectPtr value = node[i].val; if (!sq_isclass(value)) continue; const char *className = _stringval(key); SQClass *cls = _class(value); classTable[cls] = { -1, className }; } getstateProcName = SQString::Create(_ss(vm), "__getstate"); setstateProcName = SQString::Create(_ss(vm), "__setstate"); return true; } bool serializeObject(const SQObject &obj) { if (sq_isnull(obj)) { uint8_t v = TP_NULL | 1; stream->Write(&v, sizeof(v)); } else if (sq_isbool(obj)) { uint8_t v = TP_BOOL | (_integer(obj) ? 1 : 0); stream->Write(&v, sizeof(v)); } else if (sq_isinteger(obj)) { uint8_t v = TP_INTEGER; SQInteger iv = _integer(obj); if (iv >= -1 && iv <= 2) { v |= iv + 1; // map -1, 0, 1, 2 to 0, 1, 2, 3 stream->Write(&v, sizeof(v)); } else if (iv >= INT8_MIN && iv <= INT8_MAX) { v |= 4; // 4 for 1-byte integer stream->Write(&v, sizeof(v)); int8_t iv8 = static_cast(iv); stream->Write(&iv8, sizeof(int8_t)); } else if (iv >= INT16_MIN && iv <= INT16_MAX) { v |= 5; // 5 for 2-byte integer stream->Write(&v, sizeof(v)); int16_t iv16 = static_cast(iv); stream->Write(&iv16, sizeof(int16_t)); } else if (iv >= INT32_MIN && iv <= INT32_MAX) { v |= 6; // 6 for 4-byte integer stream->Write(&v, sizeof(v)); int32_t iv32 = static_cast(iv); stream->Write(&iv, sizeof(int32_t)); } else { v |= 7; // 7 for 8-byte integer stream->Write(&v, sizeof(v)); int64_t iv64 = static_cast(iv); stream->Write(&iv64, sizeof(int64_t)); } } else if (sq_isfloat(obj)) { uint8_t v = TP_FLOAT; SQFloat fv = _float(obj); if (fv == SQFloat(-1.0)) { v |= 0; // -1.0 stream->Write(&v, sizeof(v)); } else if (fv == SQFloat(-0.5)) { v |= 1; // -0.5 stream->Write(&v, sizeof(v)); } else if (fv == SQFloat(0.0)) { v |= 2; // 0.0 stream->Write(&v, sizeof(v)); } else if (fv == SQFloat(0.5)) { v |= 3; // 0.5 stream->Write(&v, sizeof(v)); } else if (fv == SQFloat(1.0)) { v |= 4; // 1.0 stream->Write(&v, sizeof(v)); } else if (fv == SQFloat(2.0)) { v |= 5; // 2.0 stream->Write(&v, sizeof(v)); } else { #ifdef SQUSEDOUBLE v |= 7; // 8-byte float stream->Write(&v, sizeof(v)); double dfv = static_cast(fv); stream->Write(&dfv, sizeof(double)); #else v |= 6; // 4-byte float stream->Write(&v, sizeof(v)); float ffv = static_cast(fv); stream->Write(&ffv, sizeof(float)); #endif } } else if (sq_isstring(obj)) { uint8_t v = TP_STRING; SQInteger len = _string(obj)->_len; if (len == 0) { v |= 0; // empty string stream->Write(&v, sizeof(v)); } else { // check if the string is new or already in the table char *str = _stringval(obj); auto it = stringTable.find(str); if (it == stringTable.end()) { if (len <= UINT8_MAX) { v |= 1; // 1-byte string stream->Write(&v, sizeof(v)); uint8_t index = static_cast(len); stream->Write(&index, sizeof(uint8_t)); stream->Write(str, len * sizeof(char)); } else if (len <= UINT16_MAX) { v |= 2; // 2-byte string stream->Write(&v, sizeof(v)); uint16_t index = static_cast(len); stream->Write(&index, sizeof(uint16_t)); stream->Write(str, len * sizeof(char)); } else { v |= 3; // 4-byte string stream->Write(&v, sizeof(v)); uint32_t index = static_cast(len); stream->Write(&index, sizeof(uint32_t)); stream->Write(str, len * sizeof(char)); } stringTable[str] = stringCounter++; } else { uint32_t index = it->second; if (index <= UINT8_MAX) { v |= 4; // 1-byte index stream->Write(&v, sizeof(v)); uint8_t idx = static_cast(index); stream->Write(&idx, sizeof(uint8_t)); } else if (index <= UINT16_MAX) { v |= 5; // 2-byte index stream->Write(&v, sizeof(v)); uint16_t idx = static_cast(index); stream->Write(&idx, sizeof(uint16_t)); } else { v |= 6; // 4-byte index stream->Write(&v, sizeof(v)); uint32_t idx = static_cast(index); stream->Write(&idx, sizeof(uint32_t)); } } } } else if (sq_isarray(obj)) { depth++; if (depth > MAX_DEPTH) { errorString = "Maximum serialization depth exceeded"; return false; } uint8_t v = TP_ARRAY; SQArray *arr = _array(obj); SQInteger size = arr->Size(); if (size == 0) { v |= 0; // empty array stream->Write(&v, sizeof(v)); } else if (size <= UINT8_MAX) { v |= 1; // 1-byte size stream->Write(&v, sizeof(v)); uint8_t size8 = static_cast(size); stream->Write(&size8, sizeof(uint8_t)); } else { v |= 2; // 4-byte size stream->Write(&v, sizeof(v)); uint32_t size32 = static_cast(size); stream->Write(&size32, sizeof(uint32_t)); } for (SQInteger i = 0; i < size; ++i) if (!serializeObject(arr->_values[i])) return false; depth--; } else if (sq_istable(obj)) { depth++; if (depth > MAX_DEPTH) { errorString = "Maximum serialization depth exceeded"; return false; } uint8_t v = TP_TABLE; SQTable *tbl = _table(obj); SQInteger size = tbl->CountUsed(); if (size == 0) { v |= 0; // empty table stream->Write(&v, sizeof(v)); } else if (size <= UINT8_MAX) { v |= 1; // 1-byte size stream->Write(&v, sizeof(v)); uint8_t size8 = static_cast(size); stream->Write(&size8, sizeof(uint8_t)); } else { v |= 2; // 4-byte size stream->Write(&v, sizeof(v)); uint32_t size32 = static_cast(size); stream->Write(&size32, sizeof(uint32_t)); } SQTable::_HashNode *node = tbl->_nodes; uint32_t count = tbl->_numofnodes_minus_one + 1; for (uint32_t i = 0; i < count; ++i) { if (sq_type(node[i].key) & OT_FREE_TABLE_SLOT) continue; if (!serializeObject(node[i].key)) return false; if (!serializeObject(node[i].val)) return false; } depth--; } else if (sq_isinstance(obj)) { depth++; if (depth > MAX_DEPTH) { errorString = "Maximum serialization depth exceeded"; return false; } SQClass *cls = _instance(obj)->_class; if (!fillAvailableClassNamesOnDemand()) return false; auto it = classTable.find(cls); if (it == classTable.end()) { errorString = "Unsupported class for serialization"; return false; } SQObjectPtr closure; if (_instance(obj)->Get(getstateProcName, closure)) { if (!sq_isclosure(closure) && !sq_isnativeclosure(closure)) { errorString = "Instance method __getstate must be a closure"; return false; } } else { errorString = "Instance must have __getstate method for serialization"; return false; } ClassDesc &classDesc = it->second; if (classDesc.index == -1) { // New class, write class name int classNameLen = strlen(classDesc.className); if (classNameLen > UINT8_MAX) { errorString = "Class name too long for serialization"; return false; } uint8_t classNameLen8 = static_cast(classNameLen); uint8_t v = TP_INSTANCE | 0x0F; // 0x0F indicates a new class stream->Write(&v, sizeof(v)); stream->Write(&classNameLen8, sizeof(classNameLen8)); stream->Write(classDesc.className, classNameLen * sizeof(char)); classDesc.index = classCounter++; if (classCounter >= 0x0EFF) { errorString = "Too many different classes for serialization"; return false; } } else { // existing class, write index assert(classDesc.index >= 0 && classDesc.index < 0x0EFF); uint8_t v = TP_INSTANCE | (0x0F & (classDesc.index >> 8)); uint8_t restOfIndex = static_cast(classDesc.index & 0xFF); stream->Write(&v, sizeof(v)); stream->Write(&restOfIndex, sizeof(restOfIndex)); } SQInteger referenceStackTop = sq_gettop(vm); int countOfParams = getCountOfParams(closure); sq_pushobject(vm, closure); sq_pushobject(vm, obj); // this if (countOfParams > 1) sq_pushobject(vm, availableClasses); SQInteger top = sq_gettop(vm); SQRESULT res = sq_call(vm, countOfParams > 1 ? 2 : 1, SQTrue, SQTrue); if (SQ_FAILED(res)) { errorString = "Failed to call __getstate method"; sq_pop(vm, countOfParams > 1 ? 2 : 1); // pop arguments return false; } SQObject state; sq_getstackobj(vm, -1, &state); if (!serializeObject(state)) return false; sq_pop(vm, 2); SQInteger newTop = sq_gettop(vm); if (newTop != referenceStackTop) { assert(!"Unexpected stack size after __getstate call"); errorString = "Unexpected stack size after __getstate call"; return false; } depth--; return true; } else { errorString = "Unsupported object type for serialization"; return false; } return true; } SQRESULT serialize(HSQUIRRELVM vm_, SQStream *dest_, SQObjectPtr obj, SQObjectPtr available_classes) { vm = vm_; stream = dest_; errorString = nullptr; stringCounter = 0; classCounter = 0; stringTable.clear(); classTable.clear(); stringList.clear(); classList.clear(); availableClasses = available_classes; depth = 0; uint8_t startMarker = START_MARKER; stream->Write(&startMarker, sizeof(startMarker)); if (!serializeObject(obj)) return sq_throwerror(vm, errorString ? errorString : "Serialization failed"); uint8_t endMarker = END_MARKER; stream->Write(&endMarker, sizeof(endMarker)); return SQ_OK; } bool unexpectedEndOfData() { errorString = "Unexpected end of data during deserialization"; return false; } bool deserializeObject() { uint8_t t = 0; if (stream->Read(&t, sizeof(t)) != sizeof(t)) return unexpectedEndOfData(); unsigned v = t & 0x0F; switch (t & 0xF0) { case TP_NULL: assert(v == 1); sq_pushnull(vm); return true; case TP_BOOL: { assert(v <= 1); sq_pushbool(vm, v); return true; } case TP_INTEGER: { switch (v) { case 0: sq_pushinteger(vm, -1); return true; case 1: sq_pushinteger(vm, 0); return true; case 2: sq_pushinteger(vm, 1); return true; case 3: sq_pushinteger(vm, 2); return true; case 4: { int8_t iv8 = 0; if (stream->Read(&iv8, sizeof(iv8)) != sizeof(iv8)) return unexpectedEndOfData(); sq_pushinteger(vm, iv8); return true; } case 5: { int16_t iv16 = 0; if (stream->Read(&iv16, sizeof(iv16)) != sizeof(iv16)) return unexpectedEndOfData(); sq_pushinteger(vm, iv16); return true; } case 6: { int32_t iv32 = 0; if (stream->Read(&iv32, sizeof(iv32)) != sizeof(iv32)) return unexpectedEndOfData(); sq_pushinteger(vm, iv32); return true; } case 7: { int64_t iv64 = 0; if (stream->Read(&iv64, sizeof(iv64)) != sizeof(iv64)) return unexpectedEndOfData(); sq_pushinteger(vm, iv64); return true; } default: errorString = "Invalid integer type during deserialization"; return false; } break; } case TP_FLOAT: { switch (v) { case 0: sq_pushfloat(vm, -1.0f); return true; case 1: sq_pushfloat(vm, -0.5f); return true; case 2: sq_pushfloat(vm, 0.0f); return true; case 3: sq_pushfloat(vm, 0.5f); return true; case 4: sq_pushfloat(vm, 1.0f); return true; case 5: sq_pushfloat(vm, 2.0f); return true; case 6: { float ffv = 0.0f; if (stream->Read(&ffv, sizeof(ffv)) != sizeof(ffv)) return unexpectedEndOfData(); sq_pushfloat(vm, static_cast(ffv)); return true; } case 7: { double dfv = 0.0; if (stream->Read(&dfv, sizeof(dfv)) != sizeof(dfv)) return unexpectedEndOfData(); sq_pushfloat(vm, static_cast(dfv)); return true; } default: errorString = "Invalid float type during deserialization"; return false; } break; } case TP_STRING: { uint32_t len = 0; const char *data = nullptr; if (v < 4) { switch (v) { case 0: sq_pushstring(vm, "", 0); return true; // Empty string case 1: { uint8_t len8 = 0; if (stream->Read(&len8, sizeof(len8)) != sizeof(len8)) return unexpectedEndOfData(); len = static_cast(len8); } break; case 2: { uint16_t len16 = 0; if (stream->Read(&len16, sizeof(len16)) != sizeof(len16)) return unexpectedEndOfData(); len = static_cast(len16); } break; case 3: { uint32_t len32 = 0; if (stream->Read(&len32, sizeof(len32)) != sizeof(len32)) return unexpectedEndOfData(); len = len32; } break; } if (len >= SQ_DESER_MAX_OBJECT_SIZE) { errorString = "String too large during deserialization"; return false; } data = sq_getscratchpad(vm, len * sizeof(char)); if (stream->Read((void *)data, len * sizeof(char)) != len * sizeof(char)) return unexpectedEndOfData(); SQObjectPtr s(SQString::Create(_ss(vm), data, len)); stringList.push_back(s); vm->Push(s); return true; } else // v >= 4 { uint32_t index = 0; if (v == 4) { uint8_t idx8 = 0; if (stream->Read(&idx8, sizeof(idx8)) != sizeof(idx8)) return unexpectedEndOfData(); index = static_cast(idx8); } else if (v == 5) { uint16_t idx16 = 0; if (stream->Read(&idx16, sizeof(idx16)) != sizeof(idx16)) return unexpectedEndOfData(); index = static_cast(idx16); } else if (v == 6) { if (stream->Read(&index, sizeof(index)) != sizeof(index)) return unexpectedEndOfData(); } else { errorString = "Invalid string type during deserialization"; return false; } if (index >= stringList.size()) { errorString = "String index out of bounds during deserialization"; return false; } vm->Push(stringList[index]); return true; } } case TP_ARRAY: { uint32_t size = 0; if (v == 0) { sq_newarray(vm, 0); // empty array return true; } else if (v == 1) { uint8_t size8 = 0; if (stream->Read(&size8, sizeof(size8)) != sizeof(size8)) return unexpectedEndOfData(); size = static_cast(size8); } else if (v == 2) { uint32_t size32 = 0; if (stream->Read(&size32, sizeof(size32)) != sizeof(size32)) return unexpectedEndOfData(); size = size32; } else { errorString = "Invalid array type during deserialization"; return false; } if (size >= SQ_DESER_MAX_OBJECT_SIZE) { errorString = "Array too large during deserialization"; return false; } sq_newarray(vm, size); for (uint32_t i = 0; i < size; ++i) { sq_pushinteger(vm, i); if (!deserializeObject()) { sq_pop(vm, 2); return false; } sq_rawset(vm, -3); } return true; } case TP_TABLE: { uint32_t size = 0; if (v == 0) { sq_newtable(vm); // empty table return true; } else if (v == 1) { uint8_t size8 = 0; if (stream->Read(&size8, sizeof(size8)) != sizeof(size8)) return unexpectedEndOfData(); size = static_cast(size8); } else if (v == 2) { uint32_t size32 = 0; if (stream->Read(&size32, sizeof(size32)) != sizeof(size32)) return unexpectedEndOfData(); size = size32; } else { errorString = "Invalid table type during deserialization"; return false; } if (size >= SQ_DESER_MAX_OBJECT_SIZE) { errorString = "Table too large during deserialization"; return false; } sq_newtableex(vm, size); for (uint32_t i = 0; i < size; ++i) { if (!deserializeObject()) { // key sq_pop(vm, 1); return false; } if (!deserializeObject()) { // value sq_pop(vm, 2); return false; } sq_rawset(vm, -3); } return true; } case TP_INSTANCE: { fillAvailableClassNamesOnDemand(); uint32_t index = 0; if (v == 0x0F) { // New class, read class name uint8_t classNameLen8 = 0; if (stream->Read(&classNameLen8, sizeof(classNameLen8)) != sizeof(classNameLen8)) return unexpectedEndOfData(); uint32_t classNameLen = static_cast(classNameLen8); char *className = sq_getscratchpad(vm, classNameLen * sizeof(char)); if (stream->Read(className, classNameLen * sizeof(char)) != classNameLen * sizeof(char)) return unexpectedEndOfData(); SQString *classNameStr = SQString::Create(_ss(vm), className, classNameLen); if (!sq_istable(availableClasses)) { errorString = "Instance found during deserialization, but available classes not set or not a table"; return false; } SQTable *tbl = _table(availableClasses); SQObjectPtr classObj; if (!tbl->Get(SQObjectPtr(classNameStr), classObj)) { errorString = "Class not found in available classes during deserialization"; return false; } assert(sq_isclass(classObj)); // must be already checked in fillAvailableClassNamesOnDemand() classList.push_back(classObj); index = classList.size() - 1; // check number of arguments required by the class constructor (must accept 1 argument - the instance itself) SQObjectPtr constructor; if (_class(classObj)->GetConstructor(constructor)) { if (!isClassConstructorValid(constructor)) return false; } } else { uint8_t restOfIndex = 0; if (stream->Read(&restOfIndex, sizeof(restOfIndex)) != sizeof(restOfIndex)) return unexpectedEndOfData(); index = ((v & 0x0F) << 8) | static_cast(restOfIndex); if (index >= classList.size()) { errorString = "Class index out of bounds during deserialization"; return false; } } SQObjectPtr classObj = classList[index]; SQObjectPtr setStateClosure; if (_class(classObj)->Get(setstateProcName, setStateClosure)) { if (!sq_isclosure(setStateClosure) && !sq_isnativeclosure(setStateClosure)) { errorString = "Class method __setstate must be a closure"; return false; } } else { errorString = "Class must have __setstate method for deserialization"; return false; } // create instance SQInteger stackbase = sq_gettop(vm); sq_pushobject(vm, classObj); sq_pushnull(vm); // environment if (SQ_FAILED(sq_call(vm, 1, SQTrue, SQTrue))) { errorString = "Failed to instantiate class during deserialization"; sq_settop(vm, stackbase); return false; } sq_remove(vm, -2); // remove class from stack, instance stays on stack sq_pushobject(vm, setStateClosure); sq_push(vm, -2); // instance if (!deserializeObject()) { sq_pop(vm, 2); return false; } int countOfSetStateParams = getCountOfParams(setStateClosure); if (countOfSetStateParams > 2) sq_pushobject(vm, availableClasses); SQInteger top = sq_gettop(vm); SQRESULT res = sq_call(vm, countOfSetStateParams > 2 ? 3 : 2, SQTrue, SQTrue); if (SQ_FAILED(res)) { errorString = "Failed to call __setstate method"; sq_settop(vm, stackbase); // restore stack return false; } sq_settop(vm, stackbase + 1); // instance is left on the stack return true; } default: errorString = "Unsupported object type during deserialization"; return false; } } bool isClassConstructorValid(SQObjectPtr &constructor) { if (sq_isclosure(constructor)) { SQFunctionProto *func = _closure(constructor)->_function; SQInteger paramssize = func->_nparameters; SQInteger ndef = func->_ndefaultparams; SQInteger nargs = 1; if (func->_varparams) paramssize--; if (paramssize != nargs && !(ndef && nargs < paramssize && (paramssize - nargs) <= ndef)) { errorString = "Class constructor must accept call with all default arguments during deserialization"; return false; } return true; } else if (sq_isnativeclosure(constructor)) { SQNativeClosure *nativeClosure = _nativeclosure(constructor); if (nativeClosure->_nparamscheck > 1) { errorString = "Class constructor must accept call with all default arguments during deserialization"; return false; } return true; } errorString = "Class constructor must be a closure or native closure during deserialization"; return false; } SQRESULT deserialize(HSQUIRRELVM vm_, SQStream *src_, SQObjectPtr available_classes) { // parsed value is left on the stack vm = vm_; stream = src_; errorString = nullptr; stringCounter = 0; classCounter = 0; stringTable.clear(); classTable.clear(); stringList.clear(); classList.clear(); availableClasses = available_classes; depth = 0; uint8_t startMarker = 0; if (stream->Read(&startMarker, sizeof(startMarker)) != sizeof(startMarker)) return sq_throwerror(vm, "Unexpected end of data during deserialization"); if (startMarker != START_MARKER) return sq_throwerror(vm, "Invalid start marker during deserialization"); if (!deserializeObject()) return sq_throwerror(vm, errorString ? errorString : "Deserialization failed"); uint8_t endMarker = 0; if (stream->Read(&endMarker, sizeof(endMarker)) != sizeof(endMarker)) return sq_throwerror(vm, "Unexpected end of data during deserialization"); if (endMarker != END_MARKER) return sq_throwerror(vm, "Invalid end marker during deserialization"); return SQ_OK; } }; SQRESULT sqstd_serialize_object_to_stream(HSQUIRRELVM vm, SQStream *dest, SQObjectPtr obj, SQObjectPtr available_classes) { SQStreamSerializer serializer; return serializer.serialize(vm, dest, obj, available_classes); } SQRESULT sqstd_deserialize_object_from_stream(HSQUIRRELVM vm, SQStream *src, SQObjectPtr available_classes) { int prevTop = sq_gettop(vm); SQStreamSerializer serializer; SQRESULT res = serializer.deserialize(vm, src, available_classes); sq_settop(vm, prevTop + 1); return res; } ================================================ FILE: sqstdlib/sqstdserialization.h ================================================ /* see copyright notice in squirrel.h */ #pragma once #include SQRESULT sqstd_serialize_object_to_stream(HSQUIRRELVM vm, SQStream *dest, SQObjectPtr obj, SQObjectPtr available_classes); SQRESULT sqstd_deserialize_object_from_stream(HSQUIRRELVM vm, SQStream *src, SQObjectPtr available_classes); ================================================ FILE: sqstdlib/sqstdstream.cpp ================================================ /* see copyright notice in squirrel.h */ #include #include #include #include #include #include #include #include #include "sqstdstream.h" #include "sqstdblobimpl.h" #include "sqstdserialization.h" #include #define SETUP_STREAM(v) \ SQStream *self = NULL; \ if(SQ_FAILED(sq_getinstanceup(v,1,(SQUserPointer*)&self,(SQUserPointer)((SQUnsignedInteger)SQSTD_STREAM_TYPE_TAG)))) \ return sq_throwerror(v,"invalid type tag"); \ if(!self || !self->IsValid()) \ return sq_throwerror(v,"the stream is invalid"); SQInteger _stream_readblob(HSQUIRRELVM v) { SETUP_STREAM(v); SQUserPointer data,blobp; SQInteger size,res; sq_getinteger(v,2,&size); if(size > self->Len()) { size = self->Len(); } data = sq_getscratchpad(v,size); res = self->Read(data,size); if(res <= 0) return sq_throwerror(v,"no data left to read"); blobp = sqstd_createblob(v,res); memcpy(blobp,data,res); return 1; } #define SAFE_READN(ptr,len) { \ if(self->Read(ptr,len) != len) return sq_throwerror(v,"io error"); \ } SQInteger _stream_readn(HSQUIRRELVM v) { SETUP_STREAM(v); SQInteger format; sq_getinteger(v, 2, &format); switch(format) { case 'l': { SQInteger i; SAFE_READN(&i, sizeof(i)); sq_pushinteger(v, i); } break; case 'i': { SQInt32 i; SAFE_READN(&i, sizeof(i)); sq_pushinteger(v, i); } break; case 's': { short s; SAFE_READN(&s, sizeof(short)); sq_pushinteger(v, s); } break; case 'w': { unsigned short w; SAFE_READN(&w, sizeof(unsigned short)); sq_pushinteger(v, w); } break; case 'c': { signed char c; SAFE_READN(&c, sizeof(signed char)); sq_pushinteger(v, c); } break; case 'b': { unsigned char c; SAFE_READN(&c, sizeof(unsigned char)); sq_pushinteger(v, c); } break; case 'f': { float f; SAFE_READN(&f, sizeof(float)); sq_pushfloat(v, f); } break; case 'd': { double d; SAFE_READN(&d, sizeof(double)); sq_pushfloat(v, (SQFloat)d); } break; default: return sq_throwerror(v, "invalid format"); } return 1; } SQInteger _stream_writeblob(HSQUIRRELVM v) { SQUserPointer data; SQInteger size; SETUP_STREAM(v); if(SQ_FAILED(sqstd_getblob(v,2,&data))) return sq_throwerror(v,"invalid parameter"); size = sqstd_getblobsize(v,2); if(self->Write(data,size) != size) return sq_throwerror(v,"io error"); sq_pushinteger(v,size); return 1; } SQInteger _stream_writestring(HSQUIRRELVM v) { const char * str; SQInteger len; SETUP_STREAM(v); if (SQ_FAILED(sq_getstringandsize(v, 2, &str, &len))) return sq_throwerror(v, "invalid parameter"); SQInteger size = len * sizeof(char); if (self->Write((void *)str, size) != size) return sq_throwerror(v,"io error"); sq_pushinteger(v, len); return 1; } SQInteger _stream_writen(HSQUIRRELVM v) { SETUP_STREAM(v); SQInteger format, ti; SQFloat tf; sq_getinteger(v, 3, &format); switch(format) { case 'l': { SQInteger i; sq_getinteger(v, 2, &ti); i = ti; self->Write(&i, sizeof(SQInteger)); } break; case 'i': { SQInt32 i; sq_getinteger(v, 2, &ti); i = (SQInt32)ti; self->Write(&i, sizeof(SQInt32)); } break; case 's': { short s; sq_getinteger(v, 2, &ti); s = (short)ti; self->Write(&s, sizeof(short)); } break; case 'w': { unsigned short w; sq_getinteger(v, 2, &ti); w = (unsigned short)ti; self->Write(&w, sizeof(unsigned short)); } break; case 'c': { signed char c; sq_getinteger(v, 2, &ti); c = (signed char)ti; self->Write(&c, sizeof(signed char)); } break; case 'b': { unsigned char b; sq_getinteger(v, 2, &ti); b = (unsigned char)ti; self->Write(&b, sizeof(unsigned char)); } break; case 'f': { float f; sq_getfloat(v, 2, &tf); f = (float)tf; self->Write(&f, sizeof(float)); } break; case 'd': { double d; sq_getfloat(v, 2, &tf); d = tf; self->Write(&d, sizeof(double)); } break; default: return sq_throwerror(v, "invalid format"); } return 0; } SQInteger _stream_seek(HSQUIRRELVM v) { SETUP_STREAM(v); SQInteger offset, origin = SQ_SEEK_SET; sq_getinteger(v, 2, &offset); if(sq_gettop(v) > 2) { SQInteger t; sq_getinteger(v, 3, &t); switch(t) { case 'b': origin = SQ_SEEK_SET; break; case 'c': origin = SQ_SEEK_CUR; break; case 'e': origin = SQ_SEEK_END; break; default: return sq_throwerror(v,"invalid origin"); } } sq_pushinteger(v, self->Seek(offset, origin)); return 1; } SQInteger _stream_tell(HSQUIRRELVM v) { SETUP_STREAM(v); sq_pushinteger(v, self->Tell()); return 1; } SQInteger _stream_len(HSQUIRRELVM v) { SETUP_STREAM(v); sq_pushinteger(v, self->Len()); return 1; } SQInteger _stream_flush(HSQUIRRELVM v) { SETUP_STREAM(v); if(!self->Flush()) sq_pushinteger(v, 1); else sq_pushnull(v); return 1; } SQInteger _stream_eos(HSQUIRRELVM v) { SETUP_STREAM(v); if(self->EOS()) sq_pushinteger(v, 1); else sq_pushnull(v); return 1; } SQInteger _stream_writeobject(HSQUIRRELVM v) { SETUP_STREAM(v); SQObjectPtr obj = stack_get(v, 2); SQObjectPtr availableClasses; if (sq_gettop(v) > 2) availableClasses = stack_get(v, 3); SQRESULT res = sqstd_serialize_object_to_stream(v, self, obj, availableClasses); return SQ_FAILED(res) ? res : 0; } SQInteger _stream_readobject(HSQUIRRELVM v) { SETUP_STREAM(v); SQObjectPtr availableClasses; if (sq_gettop(v) > 1) availableClasses = stack_get(v, 2); SQRESULT res = sqstd_deserialize_object_from_stream(v, self, availableClasses); return SQ_FAILED(res) ? res : 1; } SQInteger _stream__cloned(HSQUIRRELVM v) { return sq_throwerror(v,"this object cannot be cloned"); } static const SQRegFunctionFromStr _stream_methods[] = { { _stream_readblob, "instance.readblob(size: int): instance", "Reads up to size bytes and returns them as a blob" }, { _stream_readn, "instance.readn(format: int): number", "Reads a value of the given numeric format and returns it" }, { _stream_writeblob, "instance.writeblob(blob: instance): int", "Writes the given blob and returns the number of bytes written" }, { _stream_writestring, "instance.writestring(str: string): int", "Writes the string and returns the number of characters written" }, { _stream_writen, "instance.writen(value: number, format: int)", "Writes a numeric value in the given format" }, { _stream_seek, "instance.seek(offset: int, [origin: int]): int", "Seeks to the given offset; origin is 'b' (begin), 'c' (current) or 'e' (end)" }, { _stream_tell, "instance.tell(): int", "Returns the current stream position" }, { _stream_len, "instance.len(): int", "Returns the stream length" }, { _stream_eos, "instance.eos(): int|null", "Returns non-null if the stream is at end-of-stream" }, { _stream_flush, "instance.flush(): int|null", "Flushes the stream and returns non-null on success" }, { _stream_writeobject, "instance.writeobject(obj, [classes: table|null])", "Serializes the object to the stream" }, { _stream_readobject, "instance.readobject([classes: table|null]): any", "Deserializes an object from the stream" }, { _stream__cloned, "instance._cloned(other)", "Stream cloning is not supported" }, { NULL, NULL, NULL } }; SQRESULT sqstd_init_streamclass(HSQUIRRELVM v) { sq_pushregistrytable(v); sq_pushstring(v,"std_stream",-1); if(SQ_FAILED(sq_get(v,-2))) { sq_pushstring(v,"std_stream",-1); sq_newclass(v,SQFalse); sq_settypetag(v,-1,(SQUserPointer)((SQUnsignedInteger)SQSTD_STREAM_TYPE_TAG)); SQInteger i = 0; while(_stream_methods[i].f) { sq_new_closure_slot_from_decl_string(v, _stream_methods[i].f, 0, _stream_methods[i].declstring, _stream_methods[i].docstring); i++; } sq_newslot(v,-3,SQFalse); // put to registry table sq_pushstring(v,"stream",-1); sq_pushstring(v,"std_stream",-1); sq_get(v,-3); sq_newslot(v,-4,SQFalse); // put to destination table (and name the class 'stream') } else { sq_pop(v,1); //result } sq_pop(v,1); return SQ_OK; } SQRESULT declare_stream(HSQUIRRELVM v,const char* name,SQUserPointer typetag,const char* reg_name,const SQRegFunctionFromStr *methods,const SQRegFunctionFromStr *globals) { if(sq_gettype(v,-1) != OT_TABLE) return sq_throwerror(v,"table expected"); SQInteger top = sq_gettop(v); //create base stream class sqstd_init_streamclass(v); sq_pushregistrytable(v); sq_pushstring(v,reg_name,-1); sq_pushstring(v,"std_stream",-1); if(SQ_SUCCEEDED(sq_get(v,-3))) { sq_newclass(v,SQTrue); sq_settypetag(v,-1,typetag); SQInteger i = 0; while(methods[i].f) { sq_new_closure_slot_from_decl_string(v, methods[i].f, 0, methods[i].declstring, methods[i].docstring); i++; } sq_newslot(v,-3,SQFalse); sq_pop(v,1); i = 0; while(globals[i].f) { sq_new_closure_slot_from_decl_string(v, globals[i].f, 0, globals[i].declstring, globals[i].docstring); i++; } //register the class in the target table sq_pushstring(v,name,-1); sq_pushregistrytable(v); sq_pushstring(v,reg_name,-1); sq_get(v,-2); sq_remove(v,-2); sq_newslot(v,-3,SQFalse); sq_settop(v,top); return SQ_OK; } sq_settop(v,top); return SQ_ERROR; } ================================================ FILE: sqstdlib/sqstdstream.h ================================================ /* see copyright notice in squirrel.h */ #ifndef _SQSTD_STREAM_H_ #define _SQSTD_STREAM_H_ SQInteger _stream_readblob(HSQUIRRELVM v); SQInteger _stream_readline(HSQUIRRELVM v); SQInteger _stream_readn(HSQUIRRELVM v); SQInteger _stream_writeblob(HSQUIRRELVM v); SQInteger _stream_writestring(HSQUIRRELVM v); SQInteger _stream_writen(HSQUIRRELVM v); SQInteger _stream_seek(HSQUIRRELVM v); SQInteger _stream_tell(HSQUIRRELVM v); SQInteger _stream_len(HSQUIRRELVM v); SQInteger _stream_eos(HSQUIRRELVM v); SQInteger _stream_flush(HSQUIRRELVM v); SQInteger _stream_writeobject(HSQUIRRELVM v); SQInteger _stream_readobject(HSQUIRRELVM v); SQRESULT declare_stream(HSQUIRRELVM v,const char* name,SQUserPointer typetag,const char* reg_name,const SQRegFunctionFromStr *methods,const SQRegFunctionFromStr *globals); #endif /*_SQSTD_STREAM_H_*/ ================================================ FILE: sqstdlib/sqstdstring.cpp ================================================ /* see copyright notice in squirrel.h */ #include #include #include #include #include #include #include #include #include #define MAX_FORMAT_LEN 20 #define MAX_WFORMAT_LEN 3 #define ADDITIONAL_FORMAT_SPACE (100*sizeof(char)) static SQUserPointer rex_typetag = NULL; static SQBool isfmtchr(char ch) { switch(ch) { case '-': case '+': case ' ': case '#': case '0': return SQTrue; } return SQFalse; } static SQInteger validate_format(HSQUIRRELVM v, char *fmt, const char *src, SQInteger n,SQInteger &width) { char *dummy; char swidth[MAX_WFORMAT_LEN]; SQInteger wc = 0; SQInteger start = n; fmt[0] = '%'; while (isfmtchr(src[n])) n++; while (sq_isdigit(src[n])) { swidth[wc] = src[n]; n++; wc++; if(wc>=MAX_WFORMAT_LEN) return sq_throwerror(v,"width format too long"); } swidth[wc] = '\0'; if(wc > 0) { width = scstrtol(swidth,&dummy,10); } else width = 0; if (src[n] == '.') { n++; wc = 0; while (sq_isdigit(src[n])) { swidth[wc] = src[n]; n++; wc++; if(wc>=MAX_WFORMAT_LEN) return sq_throwerror(v,"precision format too long"); } swidth[wc] = '\0'; if(wc > 0) { width += scstrtol(swidth,&dummy,10); } } if (n-start > MAX_FORMAT_LEN ) return sq_throwerror(v,"format too long"); memcpy(&fmt[1],&src[start],((n-start)+1)*sizeof(char)); fmt[(n-start)+2] = '\0'; return n; } SQRESULT sqstd_format(HSQUIRRELVM v,SQInteger nformatstringidx,SQInteger *outlen,char **output) { const char *format; char *dest; char fmt[MAX_FORMAT_LEN + 5]; const SQRESULT res = sq_getstring(v,nformatstringidx,&format); if (SQ_FAILED(res)) { return res; // propagate the error } SQInteger format_size = sq_getsize(v,nformatstringidx); SQInteger allocated = (format_size+2)*sizeof(char); dest = sq_getscratchpad(v,allocated); SQInteger n = 0,i = 0, nparam = nformatstringidx+1, w = 0; //while(format[n] != '\0') while(n < format_size) { if(format[n] != '%') { assert(i < allocated); dest[i++] = format[n]; n++; } else if(format[n+1] == '%') { //handles %% dest[i++] = '%'; n += 2; } else { n++; if( nparam > sq_gettop(v) ) return sq_throwerror(v,"not enough parameters for the given format string"); n = validate_format(v,fmt,format,n,w); if(n < 0) return -1; SQInteger addlen = 0; SQInteger valtype = 0; const char *ts = NULL; SQInteger ti = 0; SQFloat tf = 0; switch(format[n]) { case 's': if(SQ_FAILED(sq_getstring(v,nparam,&ts))) return sq_throwerror(v,"string expected for the specified format"); addlen = (sq_getsize(v,nparam)*sizeof(char))+((w+1)*sizeof(char)); valtype = 's'; break; case 'i': case 'd': case 'o': case 'u': case 'x': case 'X': #ifdef _SQ64 { size_t flen = strlen(fmt); SQInteger fpos = flen - 1; char f = fmt[fpos]; const char *prec = (const char *)_PRINT_INT_PREC; while(*prec != '\0') { fmt[fpos++] = *prec++; } fmt[fpos++] = f; fmt[fpos++] = '\0'; } #endif case 'c': if(SQ_FAILED(sq_getinteger(v,nparam,&ti))) return sq_throwerror(v,"integer expected for the specified format"); addlen = (ADDITIONAL_FORMAT_SPACE)+((w+1)*sizeof(char)); valtype = 'i'; break; case 'f': case 'g': case 'G': case 'e': case 'E': if(SQ_FAILED(sq_getfloat(v,nparam,&tf))) return sq_throwerror(v,"float expected for the specified format"); addlen = (ADDITIONAL_FORMAT_SPACE)+((w+1)*sizeof(char)); valtype = 'f'; break; default: return sq_throwerror(v,"invalid format"); } n++; allocated += addlen + sizeof(char); dest = sq_getscratchpad(v,allocated); switch(valtype) { case 's': i += scsprintf(&dest[i],allocated,fmt,ts); break; case 'i': i += scsprintf(&dest[i],allocated,fmt,ti); break; case 'f': { int len = scsprintf(&dest[i], allocated, fmt, tf); for (; len > 0; len--, i++) if (dest[i] == ',') dest[i] = '.'; break; } }; nparam ++; } } *outlen = i; dest[i] = '\0'; *output = dest; return SQ_OK; } void sqstd_pushstringf(HSQUIRRELVM v,const char *s,...) { SQInteger n=256; va_list args; begin: va_start(args,s); char *b=sq_getscratchpad(v,n); SQInteger r=vsnprintf(b,n,s,args); va_end(args); if (r>=n) { n=r+1;//required+null goto begin; } else if (r<0) { sq_pushnull(v); } else { sq_pushstring(v,b,r); } } static SQInteger _string_printf(HSQUIRRELVM v) { char *dest = NULL; SQInteger length = 0; if(SQ_FAILED(sqstd_format(v,2,&length,&dest))) return -1; SQPRINTFUNCTION printfunc = sq_getprintfunc(v); if(printfunc) printfunc(v,"%s",dest); return 0; } static SQInteger _string_format(HSQUIRRELVM v) { char *dest = NULL; SQInteger length = 0; if(SQ_FAILED(sqstd_format(v,2,&length,&dest))) return -1; sq_pushstring(v,dest,length); return 1; } #define IMPL_STRING_FUNC(name) static SQInteger _string_##name(HSQUIRRELVM v) { return _sq_string_ ## name ## _impl(v, 2); } IMPL_STRING_FUNC(strip) IMPL_STRING_FUNC(lstrip) IMPL_STRING_FUNC(rstrip) IMPL_STRING_FUNC(split_by_chars) IMPL_STRING_FUNC(escape) IMPL_STRING_FUNC(startswith) IMPL_STRING_FUNC(endswith) #undef IMPL_STRING_FUNC #define SETUP_REX(v) \ SQRex *self = NULL; \ if(SQ_FAILED(sq_getinstanceup(v,1,(SQUserPointer *)&self,rex_typetag))) { \ return sq_throwerror(v,"invalid type tag"); \ } static SQInteger _rexobj_releasehook(HSQUIRRELVM SQ_UNUSED_ARG(vm), SQUserPointer p, SQInteger SQ_UNUSED_ARG(size)) { SQRex *self = ((SQRex *)p); sqstd_rex_free(self); return 1; } static SQInteger _regexp_match(HSQUIRRELVM v) { SETUP_REX(v); const char *str; sq_getstring(v,2,&str); if(sqstd_rex_match(self,str) == SQTrue) { sq_pushbool(v,SQTrue); return 1; } sq_pushbool(v,SQFalse); return 1; } static void _addrexmatch(HSQUIRRELVM v,const char *str,const char *begin,const char *end) { sq_newtable(v); sq_pushstring(v,"begin",-1); sq_pushinteger(v,begin - str); sq_rawset(v,-3); sq_pushstring(v,"end",-1); sq_pushinteger(v,end - str); sq_rawset(v,-3); } static SQInteger _regexp_search(HSQUIRRELVM v) { SETUP_REX(v); const char *str,*begin,*end; SQInteger start = 0; sq_getstring(v,2,&str); if(sq_gettop(v) > 2) sq_getinteger(v,3,&start); if(sqstd_rex_search(self,str+start,&begin,&end) == SQTrue) { _addrexmatch(v,str,begin,end); return 1; } return 0; } static SQInteger _regexp_capture(HSQUIRRELVM v) { SETUP_REX(v); const char *str,*begin,*end; SQInteger start = 0; sq_getstring(v,2,&str); if(sq_gettop(v) > 2) sq_getinteger(v,3,&start); if(sqstd_rex_search(self,str+start,&begin,&end) == SQTrue) { SQInteger n = sqstd_rex_getsubexpcount(self); SQRexMatch match; sq_newarray(v,0); for(SQInteger i = 0;i < n; i++) { sqstd_rex_getsubexp(self,i,&match); if(match.len > 0) _addrexmatch(v,str,match.begin,match.begin+match.len); else _addrexmatch(v,str,str,str); //empty match sq_arrayappend(v,-2); } return 1; } return 0; } static SQInteger _regexp_subexpcount(HSQUIRRELVM v) { SETUP_REX(v); sq_pushinteger(v,sqstd_rex_getsubexpcount(self)); return 1; } static SQInteger _regexp_constructor(HSQUIRRELVM v) { SQRex *self = NULL; if (SQ_FAILED(sq_getinstanceup(v, 1, (SQUserPointer *)&self, rex_typetag))) { return sq_throwerror(v, "invalid type tag"); } if (self != NULL) { return sq_throwerror(v, "invalid regexp object"); } const char *error,*pattern; sq_getstring(v,2,&pattern); SQRex *rex = sqstd_rex_compile(sq_getallocctx(v),pattern,&error); if(!rex) return sq_throwerror(v,error); sq_setinstanceup(v,1,rex); sq_setreleasehook(v,1,_rexobj_releasehook); return 0; } static SQInteger _regexp__typeof(HSQUIRRELVM v) { sq_pushstring(v,"regexp",-1); return 1; } static const SQRegFunctionFromStr rexobj_funcs[] = { { _regexp_constructor, "constructor(pattern: string): instance" }, { _regexp_match, "instance.match(str: string): bool" }, { _regexp_search, "instance.search(str: string, [start: int]): table|null" }, { _regexp_capture, "instance.capture(str: string, [start: int]): array|null" }, { _regexp_subexpcount, "instance.subexpcount(): int" }, { _regexp__typeof, "instance._typeof(): string" }, { NULL, NULL } }; static const SQRegFunctionFromStr stringlib_funcs[] = { { _string_format, "pure format(fmt: string, ...): string" }, { _string_printf, "printf(fmt: string, ...)" }, { _string_strip, "pure strip(str: string): string" }, { _string_lstrip, "pure lstrip(str: string): string" }, { _string_rstrip, "pure rstrip(str: string): string" }, { _string_split_by_chars, "split_by_chars(str: string, separators: string, [skip_empty: bool]): array" }, { _string_escape, "pure escape(str: string): string" }, { _string_startswith, "pure startswith(str: string, prefix: string): bool" }, { _string_endswith, "pure endswith(str: string, suffix: string): bool" }, { NULL, NULL }, }; #undef _DECL_FUNC SQRESULT sqstd_register_stringlib(HSQUIRRELVM v) { sq_pushstring(v, "regexp", -1); sq_newclass(v, SQFalse); rex_typetag = (SQUserPointer)rexobj_funcs; sq_settypetag(v, -1, rex_typetag); SQInteger i = 0; while (rexobj_funcs[i].f) { sq_new_closure_slot_from_decl_string(v, rexobj_funcs[i].f, 0, rexobj_funcs[i].declstring, rexobj_funcs[i].docstring); i++; } sq_newslot(v, -3, SQFalse); i = 0; while (stringlib_funcs[i].f) { sq_new_closure_slot_from_decl_string(v, stringlib_funcs[i].f, 0, stringlib_funcs[i].declstring, stringlib_funcs[i].docstring); i++; } return SQ_OK; } ================================================ FILE: sqstdlib/sqstdsystem.cpp ================================================ /* see copyright notice in squirrel.h */ #include #include #include #include #include static SQInteger _system_getenv(HSQUIRRELVM v) { const char *s; if(SQ_SUCCEEDED(sq_getstring(v,2,&s))){ sq_pushstring(v,getenv(s),-1); return 1; } return 0; } static SQInteger _system_system(HSQUIRRELVM v) { const char *s; if(SQ_SUCCEEDED(sq_getstring(v,2,&s))){ sq_pushinteger(v,system(s)); return 1; } return sq_throwerror(v,"wrong param"); } static SQInteger _system_setenv(HSQUIRRELVM v) { const char *envname,*envval; sq_getstring(v,2,&envname); sq_getstring(v,3,&envval); #if defined(_MSC_VER) int res = _putenv_s(envname,envval); #else int res = setenv(envname,envval,1); #endif if (res != 0) return sq_throwerror(v,"setenv() failed"); return 0; } static SQInteger _system_remove(HSQUIRRELVM v) { const char *s; sq_getstring(v,2,&s); if(remove(s)==-1) return sq_throwerror(v,"remove() failed"); return 0; } static SQInteger _system_rename(HSQUIRRELVM v) { const char *oldn,*newn; sq_getstring(v,2,&oldn); sq_getstring(v,3,&newn); if(rename(oldn,newn)==-1) return sq_throwerror(v,"rename() failed"); return 0; } static const SQRegFunctionFromStr systemlib_funcs[] = { { _system_getenv, "getenv(name: string): string|null", "Returns the value of the environment variable or null" }, { _system_setenv, "setenv(name: string, value: string)", "Sets the environment variable to the given value" }, { _system_system, "system(cmd: string): int", "Executes a shell command and returns its exit code" }, { _system_remove, "remove(path: string)", "Deletes the file at the given path, throws error in case of fail" }, { _system_rename, "rename(old: string, new: string)", "Renames the file from old to new path, throws error in case of fail" }, { NULL, NULL, NULL } }; #undef _DECL_FUNC SQRESULT sqstd_register_command_line_args(HSQUIRRELVM v, int argc, char ** argv) { sq_pushroottable(v); sq_pushstring(v, "__argv", -1); sq_newarray(v, 0); for (int idx = 0; idx < argc; idx++) { sq_pushstring(v, argv[idx], -1); sq_arrayappend(v, -2); } sq_newslot(v, -3, SQFalse); sq_pop(v,1); return SQ_OK; } SQRESULT sqstd_register_systemlib(HSQUIRRELVM v) { SQInteger i = 0; while (systemlib_funcs[i].f) { sq_new_closure_slot_from_decl_string(v, systemlib_funcs[i].f, 0, systemlib_funcs[i].declstring, systemlib_funcs[i].docstring); i++; } return SQ_OK; } ================================================ FILE: squirrel/CMakeLists.txt ================================================ set(SQUIRREL_SRC sqapi.cpp sqbaselib.cpp sqclass.cpp sqdebug.cpp sqmem.cpp sqobject.cpp sqstate.cpp sqtable.cpp sqvm.cpp sqdedupshrinker.cpp sqstringlib.cpp sqext.cpp) if (ENABLE_VAR_TRACE) list(APPEND SQUIRREL_SRC vartrace.cpp) add_definitions(-DSQ_VAR_TRACE_ENABLED=1) else () list(APPEND SQUIRREL_SRC vartracestub.cpp) add_definitions(-DSQ_VAR_TRACE_ENABLED=0) endif() add_library(squirrel STATIC ${SQUIRREL_SRC}) add_library(squirrel::squirrel ALIAS squirrel) target_include_directories(squirrel PUBLIC "$" "$" PRIVATE "$" "$" ) ================================================ FILE: squirrel/ast_tools/ast_indent_render.h ================================================ #pragma once #include // for snprintf #include "sqio.h" #include #include namespace SQCompilation { class IndentedTreeRenderer : public Visitor { OutputStream* _out; SQInteger _indent; sqvector indents; sqvector vertLines; bool collectIndentsPass; int lineNum; char loc[32]; char scratchpad[1024]; void indent() { if (collectIndentsPass) { indents.push_back(_indent); return; } while (vertLines.size() <= _indent) vertLines.push_back(0); if (_indent > 0) { if (lineNum == 0 || (_indent > indents[lineNum - 1])) { int breakPos = lineNum; for (int i = lineNum; i < indents.size(); i++) { if (indents[i] == _indent) breakPos = i; if (indents[i] < _indent) break; } vertLines[_indent - 1] = breakPos - lineNum + 1; if (vertLines[_indent - 1] == 1) vertLines[_indent - 1] = 0; } for (SQInteger i = 0; i < _indent; ++i) { if (vertLines[i] > 1) _out->writeString(i == _indent - 1 ? "|-" : "| "); else if (vertLines[i] == 1) _out->writeString("`-"); else _out->writeString(" "); if (vertLines[i] > 0) vertLines[i]--; } } lineNum++; } const char* treeopToStr(TreeOp op) { switch (op) { case TO_NULLC: return "??"; case TO_ASSIGN: return "="; case TO_OROR: return "||"; case TO_ANDAND: return "&&"; case TO_OR: return "|"; case TO_XOR: return "^"; case TO_AND: return "&"; case TO_NE: return "!="; case TO_EQ: return "=="; case TO_3CMP: return "<=>"; case TO_GE: return ">="; case TO_GT: return ">"; case TO_LE: return "<="; case TO_LT: return "<"; case TO_IN: return "IN"; case TO_INSTANCEOF: return "INSTANCEOF"; case TO_USHR: return ">>>"; case TO_SHR: return ">>"; case TO_SHL: return "<<"; case TO_MUL: return "*"; case TO_DIV: return "/"; case TO_MOD: return "%"; case TO_ADD: return "+"; case TO_SUB: case TO_NEG: return "-"; case TO_NOT: return "!"; case TO_BNOT: return "~"; case TO_TYPEOF: return "TYPEOF"; case TO_RESUME: return "RESUME"; case TO_CLONE: return "CLONE"; case TO_DELETE: return "DELETE"; case TO_NEWSLOT: return "<-"; case TO_PLUSEQ: return "+="; case TO_MINUSEQ: return "-="; case TO_MULEQ: return "*="; case TO_DIVEQ: return "/="; case TO_MODEQ: return "%="; case TO_STATIC_MEMO: return "STATIC_MEMO"; case TO_INLINE_CONST: return "INLINE_CONST"; case TO_PAREN: return "("; default: return ""; } } void writeFmtString(const char* fmt, ...) { if (collectIndentsPass) return; char buf[256]; va_list args; va_start(args, fmt); vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); _out->writeString(buf); } void writeIndentedFmtString(const char* fmt, ...) { indent(); if (collectIndentsPass) return; char buf[256]; va_list args; va_start(args, fmt); vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); _out->writeString(buf); } public: bool printNodesLocation; IndentedTreeRenderer(OutputStream* output) : _out(output), _indent(0), indents(nullptr), vertLines(nullptr), collectIndentsPass(false), printNodesLocation(true), lineNum(0), loc(""), scratchpad("") {} void render(Node* n) { if (n) { collectIndentsPass = true; n->visit(this); collectIndentsPass = false; n->visit(this); } else { indent(); _out->writeString("(null)"); } } virtual void visitNode(Node* node) override { TreeOp op = node->op(); if (printNodesLocation && !collectIndentsPass) snprintf(loc, sizeof(loc), " [%d:%d]", int(node->lineStart()), int(node->columnStart())); else loc[0] = 0; switch (op) { case TO_ID: { Id* id = static_cast(node); writeIndentedFmtString("Id '%s'%s\n", id->name(), loc); break; } case TO_LITERAL: { LiteralExpr* lit = static_cast(node); switch (lit->kind()) { case LK_STRING: writeIndentedFmtString("LiteralExpr LK_STRING \"%s\"%s\n", lit->s(), loc); break; case LK_FLOAT: writeIndentedFmtString("LiteralExpr LK_FLOAT %f%s\n", lit->f(), loc); break; case LK_INT: writeIndentedFmtString("LiteralExpr LK_INT %lld%s\n", (long long)lit->i(), loc); break; case LK_BOOL: writeIndentedFmtString("LiteralExpr LK_BOOL %s%s\n", lit->b() ? "true" : "false", loc); break; case LK_NULL: writeIndentedFmtString("LiteralExpr LK_NULL%s\n", loc); break; default: writeIndentedFmtString("LiteralExpr %s\n", loc); break; } break; } case TO_NOT: case TO_BNOT: case TO_NEG: case TO_TYPEOF: case TO_RESUME: case TO_CLONE: case TO_PAREN: case TO_DELETE: case TO_STATIC_MEMO: case TO_INLINE_CONST: { UnExpr* expr = static_cast(node); writeIndentedFmtString("UnExpr '%s'%s\n", treeopToStr(expr->op()), loc); ++_indent; expr->argument()->visit(this); --_indent; break; } case TO_CODE_BLOCK_EXPR: { CodeBlockExpr* expr = static_cast(node); writeIndentedFmtString("CodeBlockExpr%s\n", loc); ++_indent; expr->block()->visit(this); --_indent; break; } case TO_NULLC: case TO_ASSIGN: case TO_OROR: case TO_ANDAND: case TO_OR: case TO_XOR: case TO_AND: case TO_NE: case TO_EQ: case TO_3CMP: case TO_GE: case TO_GT: case TO_LE: case TO_LT: case TO_IN: case TO_INSTANCEOF: case TO_USHR: case TO_SHR: case TO_SHL: case TO_MUL: case TO_DIV: case TO_MOD: case TO_ADD: case TO_SUB: case TO_NEWSLOT: case TO_PLUSEQ: case TO_MINUSEQ: case TO_MULEQ: case TO_DIVEQ: case TO_MODEQ: { BinExpr* expr = static_cast(node); writeIndentedFmtString("BinExpr '%s'%s\n", treeopToStr(expr->op()), loc); ++_indent; writeIndentedFmtString("Left\n"); ++_indent; expr->lhs()->visit(this); --_indent; writeIndentedFmtString("Right\n"); ++_indent; expr->rhs()->visit(this); --_indent; --_indent; break; } case TO_TERNARY: { TerExpr* expr = static_cast(node); writeIndentedFmtString("TerExpr%s\n", loc); ++_indent; writeIndentedFmtString("Condition\n"); ++_indent; expr->a()->visit(this); --_indent; writeIndentedFmtString("TrueBranch\n"); ++_indent; expr->b()->visit(this); --_indent; writeIndentedFmtString("FalseBranch\n"); ++_indent; expr->c()->visit(this); --_indent; --_indent; break; } case TO_CALL: { CallExpr* expr = static_cast(node); writeIndentedFmtString("CallExpr%s\n", loc); ++_indent; writeIndentedFmtString("Callee\n"); ++_indent; expr->callee()->visit(this); --_indent; --_indent; if (!expr->arguments().empty()) { ++_indent; writeIndentedFmtString("Arguments\n"); ++_indent; for (int i = 0; i < (int)expr->arguments().size(); ++i) { writeIndentedFmtString("Argument #%d\n", i + 1); ++_indent; expr->arguments()[i]->visit(this); --_indent; } --_indent; --_indent; } break; } case TO_GETFIELD: { GetFieldExpr* expr = static_cast(node); writeIndentedFmtString("GetFieldExpr '%s' fieldName = '%s'%s\n", expr->isNullable() ? "?.":".", expr->fieldName(), loc); ++_indent; writeIndentedFmtString("Receiver\n"); ++_indent; expr->receiver()->visit(this); --_indent; --_indent; break; } case TO_SETFIELD: { SetFieldExpr* expr = static_cast(node); writeIndentedFmtString("SetFieldExpr '%s' fieldName = '%s'%s\n", expr->isNullable() ? "?." : ".", expr->fieldName(), loc); ++_indent; writeIndentedFmtString("Receiver\n"); ++_indent; expr->receiver()->visit(this); --_indent; --_indent; break; } case TO_GETSLOT: { GetSlotExpr* expr = static_cast(node); writeIndentedFmtString("GetSlotExpr '%s'%s\n", expr->isNullable() ? "?[" : "[", loc); ++_indent; writeIndentedFmtString("Receiver\n"); ++_indent; expr->receiver()->visit(this); --_indent; writeIndentedFmtString("Key\n"); ++_indent; expr->key()->visit(this); --_indent; --_indent; break; } case TO_SETSLOT: { SetSlotExpr* expr = static_cast(node); writeIndentedFmtString("SetSlotExpr '%s'%s\n", expr->isNullable() ? "?[" : "[", loc); ++_indent; writeIndentedFmtString("Receiver\n"); ++_indent; expr->receiver()->visit(this); --_indent; writeIndentedFmtString("Key\n"); ++_indent; expr->key()->visit(this); --_indent; writeIndentedFmtString("Value\n"); ++_indent; expr->value()->visit(this); --_indent; --_indent; break; } case TO_INC: { IncExpr* expr = static_cast(node); writeIndentedFmtString("IncExpr %s '%s'%s\n", (expr->form() == IF_PREFIX) ? "IF_PREFIX" : "IF_POSTFIX", (expr->diff() < 0) ? "--" : "++", loc); ++_indent; expr->argument()->visit(this); --_indent; break; } case TO_ARRAY: { ArrayExpr* expr = static_cast(node); writeIndentedFmtString("ArrayExpr%s\n", loc); if (!expr->initializers().empty()) { ++_indent; for (int i = 0; i < (int)expr->initializers().size(); ++i) { writeIndentedFmtString("Element #%d\n", i + 1); ++_indent; expr->initializers()[i]->visit(this); --_indent; } --_indent; } break; } case TO_COMMA: { CommaExpr* expr = static_cast(node); writeIndentedFmtString("CommaExpr%s\n", loc); ++_indent; for (int i = 0; i < (int)expr->expressions().size(); ++i) { writeIndentedFmtString("Expression #%d\n", i + 1); ++_indent; expr->expressions()[i]->visit(this); --_indent; } --_indent; break; } case TO_BLOCK: { Block* block = static_cast(node); writeIndentedFmtString("Block isRoot = %d, isBody = %d%s\n", int(block->isRoot()), int(block->isBody()), loc); ++_indent; for (Statement* stmt : block->statements()) { stmt->visit(this); } --_indent; break; } case TO_IF: { IfStatement* stmt = static_cast(node); writeIndentedFmtString("IfStatement%s\n", loc); ++_indent; writeIndentedFmtString("Condition\n"); ++_indent; stmt->condition()->visit(this); --_indent; writeIndentedFmtString("ThenBranch\n"); ++_indent; stmt->thenBranch()->visit(this); --_indent; if (stmt->elseBranch()) { writeIndentedFmtString("ElseBranch\n"); ++_indent; stmt->elseBranch()->visit(this); --_indent; } --_indent; break; } case TO_WHILE: { WhileStatement* loop = static_cast(node); writeIndentedFmtString("WhileStatement%s\n", loc); ++_indent; writeIndentedFmtString("Condition\n"); ++_indent; loop->condition()->visit(this); --_indent; writeIndentedFmtString("Body\n"); ++_indent; loop->body()->visit(this); --_indent; --_indent; break; } case TO_DOWHILE: { DoWhileStatement* loop = static_cast(node); writeIndentedFmtString("DoWhileStatement%s\n", loc); ++_indent; writeIndentedFmtString("Body\n"); ++_indent; loop->body()->visit(this); --_indent; writeIndentedFmtString("Condition\n"); ++_indent; loop->condition()->visit(this); --_indent; --_indent; break; } case TO_FOR: { ForStatement* loop = static_cast(node); writeIndentedFmtString("ForStatement%s\n", loc); ++_indent; if (loop->initializer()) { writeIndentedFmtString("Initializer\n"); ++_indent; loop->initializer()->visit(this); --_indent; } if (loop->condition()) { writeIndentedFmtString("Condition\n"); ++_indent; loop->condition()->visit(this); --_indent; } if (loop->modifier()) { writeIndentedFmtString("Modifier\n"); ++_indent; loop->modifier()->visit(this); --_indent; } writeIndentedFmtString("Body\n"); ++_indent; loop->body()->visit(this); --_indent; --_indent; break; } case TO_FOREACH: { ForeachStatement* loop = static_cast(node); writeIndentedFmtString("ForeachStatement%s\n", loc); ++_indent; if (loop->idx()) { writeIndentedFmtString("IndexVariable\n"); ++_indent; loop->idx()->visit(this); --_indent; } writeIndentedFmtString("ValueVariable\n"); ++_indent; loop->val()->visit(this); --_indent; writeIndentedFmtString("Container\n"); ++_indent; loop->container()->visit(this); --_indent; writeIndentedFmtString("Body\n"); ++_indent; loop->body()->visit(this); --_indent; --_indent; break; } case TO_TRY: { TryStatement* tr = static_cast(node); writeIndentedFmtString("TryStatement%s\n", loc); ++_indent; writeIndentedFmtString("TryBlock\n"); ++_indent; tr->tryStatement()->visit(this); --_indent; writeIndentedFmtString("CatchBlock exceptionId = '%s'\n", tr->exceptionId()->name()); ++_indent; tr->catchStatement()->visit(this); --_indent; --_indent; break; } case TO_RETURN: { ReturnStatement* ret = static_cast(node); writeIndentedFmtString("ReturnStatement%s\n", loc); if (ret->argument()) { ++_indent; ret->argument()->visit(this); --_indent; } break; } case TO_THROW: { ThrowStatement* thr = static_cast(node); writeIndentedFmtString("ThrowStatement%s\n", loc); if (thr->argument()) { ++_indent; thr->argument()->visit(this); --_indent; } break; } case TO_YIELD: { YieldStatement* yield = static_cast(node); writeIndentedFmtString("YieldStatement%s\n", loc); if (yield->argument()) { ++_indent; yield->argument()->visit(this); --_indent; } break; } case TO_BREAK: { writeIndentedFmtString("BreakStatement%s\n", loc); break; } case TO_CONTINUE: { writeIndentedFmtString("ContinueStatement%s\n", loc); break; } case TO_EMPTY: { writeIndentedFmtString("EmptyStatement%s\n", loc); break; } case TO_DIRECTIVE: { writeIndentedFmtString("DirectiveStmt%s\n", loc); break; } case TO_BASE: { writeIndentedFmtString("BaseExpr%s\n", loc); break; } case TO_EXPR_STMT: { ExprStatement* estmt = static_cast(node); writeIndentedFmtString("ExprStatement%s\n", loc); ++_indent; estmt->expression()->visit(this); --_indent; break; } case TO_VAR: { VarDecl* decl = static_cast(node); sq_stringify_type_mask(scratchpad, sizeof(scratchpad), decl->getTypeMask()); const char* kind = decl->isAssignable() ? "local" : "let"; writeIndentedFmtString("VarDecl '%s', name = '%s', type = '%s'%s\n", kind, decl->name(), scratchpad, loc); if (decl->expression()) { ++_indent; writeIndentedFmtString("Initializer\n"); ++_indent; decl->expression()->visit(this); --_indent; --_indent; } break; } case TO_DECL_GROUP: { DeclGroup* dgrp = static_cast(node); writeIndentedFmtString("DeclGroup%s\n", loc); ++_indent; for (auto& decl : dgrp->declarations()) { decl->visit(this); } --_indent; break; } case TO_ROOT_TABLE_ACCESS: { writeIndentedFmtString("RootTableAccessExpr%s\n", loc); break; } case TO_DESTRUCTURE: { DestructuringDecl* dstr = static_cast(node); writeIndentedFmtString("DestructuringDecl type = '%s'%s\n", dstr->type() == DT_ARRAY ? "array" : (dstr->type() == DT_TABLE ? "table" : "unknown"), loc); ++_indent; if (dstr->initExpression()) { writeIndentedFmtString("Initializer\n"); ++_indent; dstr->initExpression()->visit(this); --_indent; } for (auto& decl : dstr->declarations()) { decl->visit(this); } --_indent; break; } case TO_CONST: { ConstDecl* constDecl = static_cast(node); writeIndentedFmtString("ConstDecl name = '%s', isGlobal = %d%s\n", constDecl->name(), int(constDecl->isGlobal()), loc); ++_indent; if (constDecl->value()) { constDecl->value()->visit(this); } else { indent(); _out->writeString("(null)\n"); } --_indent; break; } case TO_ENUM: { EnumDecl* enm = static_cast(node); writeIndentedFmtString("EnumDecl name = '%s', isGlobal = %d%s\n", enm->name(), int(enm->isGlobal()), loc); ++_indent; for (auto& c : enm->consts()) { writeIndentedFmtString("Member '%s'\n", c.id); ++_indent; if (c.val) { c.val->visit(this); } else { indent(); _out->writeString("(null)\n"); } --_indent; } --_indent; break; } case TO_TABLE: { TableExpr* tbl = static_cast(node); writeIndentedFmtString("TableExpr%s\n", loc); ++_indent; for (auto& m : tbl->members()) { writeIndentedFmtString("Field\n"); ++_indent; writeIndentedFmtString("Key\n"); ++_indent; m.key->visit(this); --_indent; writeIndentedFmtString("Value\n"); ++_indent; m.value->visit(this); --_indent; --_indent; } --_indent; break; } case TO_CLASS: { ClassExpr* cls = static_cast(node); writeIndentedFmtString("ClassExpr%s\n", loc); if (cls->classKey()) { ++_indent; writeIndentedFmtString("Key\n"); ++_indent; cls->classKey()->visit(this); --_indent; --_indent; } else { ++_indent; writeIndentedFmtString("Key: \n"); --_indent; } if (cls->classBase()) { ++_indent; writeIndentedFmtString("Base\n"); ++_indent; cls->classBase()->visit(this); --_indent; --_indent; } ++_indent; writeIndentedFmtString("Members\n"); ++_indent; for (auto& member : cls->members()) { member.value->visit(this); } --_indent; --_indent; break; } case TO_FUNCTION: { FunctionExpr* fn = static_cast(node); sq_stringify_type_mask(scratchpad, sizeof(scratchpad), fn->getResultTypeMask()); writeIndentedFmtString("%s name = '%s', pure = %d, nodiscard = %d, resultType = '%s'%s\n", fn->isLambda() ? "FunctionExpr 'lambda'" : "FunctionExpr", fn->name() ? fn->name() : "", fn->isPure(), fn->isNodiscard(), scratchpad, loc); ++_indent; writeIndentedFmtString("Parameters count = %d\n", (int)fn->parameters().size()); ++_indent; for (int i = 0; i < (int)fn->parameters().size(); ++i) { sq_stringify_type_mask(scratchpad, sizeof(scratchpad), fn->parameters()[i]->getTypeMask()); writeIndentedFmtString("Parameter #%d name = '%s', type = '%s'%s%s\n", i + 1, fn->parameters()[i]->name(), scratchpad, fn->parameters()[i]->isVararg() ? " (vararg)" : "", fn->parameters()[i]->hasDefaultValue() ? " (has default)" : ""); if (fn->parameters()[i]->hasDefaultValue()) { ++_indent; writeIndentedFmtString("DefaultValue\n"); ++_indent; fn->parameters()[i]->defaultValue()->visit(this); --_indent; --_indent; } if (fn->parameters()[i]->getDestructuring()) { ++_indent; writeIndentedFmtString("Destructuring\n"); ++_indent; fn->parameters()[i]->getDestructuring()->visit(this); --_indent; --_indent; } } if (fn->isVararg()) { writeIndentedFmtString("Vararg...\n"); } --_indent; if (fn->body()) { writeIndentedFmtString("Body\n"); ++_indent; fn->body()->visit(this); --_indent; } else { writeIndentedFmtString("Body: \n"); } --_indent; break; } case TO_IMPORT: { ImportStmt* imp = static_cast(node); writeIndentedFmtString("ImportStmt module '%s'", imp->moduleName); if (imp->moduleAlias) writeFmtString(" as '%s'", imp->moduleAlias); writeFmtString("%s\n", loc); if (!imp->slots.empty()) { ++_indent; for (const SQModuleImportSlot& slot : imp->slots) { writeIndentedFmtString("'%s'", slot.name); if (slot.alias) writeFmtString(" as '%s'", slot.alias); writeFmtString("\n"); } --_indent; } break; } default: { writeIndentedFmtString("ERROR: Unknown node type, op = %d%s\n", (int)op, loc); break; } } } }; } // namespace SQCompilation ================================================ FILE: squirrel/compiler/CMakeLists.txt ================================================ set(COMPILER_SRC ast.cpp compilationcontext.cpp parser.cpp codegen.cpp constgen.cpp sqio.cpp compiler.cpp sqfuncstate.cpp sqdump.cpp optimizer.cpp lexer.cpp sqtypeparser.cpp typeinference.cpp optimizations/closureHoisting.cpp static_analyzer/analyzer.cpp static_analyzer/checker_visitor.cpp static_analyzer/config.cpp static_analyzer/function_ret_type_eval.cpp static_analyzer/global_state.cpp static_analyzer/name_shadowing_checker.cpp static_analyzer/naming.cpp static_analyzer/value_ref.cpp static_analyzer/var_scope.cpp ) add_library(quirrel-compiler STATIC ${COMPILER_SRC}) add_library(quirrel::compiler ALIAS quirrel-compiler) # The compiler needs exceptions for error handling (CompilerError) target_compile_options(quirrel-compiler PRIVATE "$<$:-fexceptions>" "$<$:/EHsc>" ) target_include_directories(quirrel-compiler PUBLIC "$" "$" PRIVATE "$" "$" "$" "$" ) ================================================ FILE: squirrel/compiler/COPYRIGHT ================================================ Copyright (c) 2003-2017 Alberto Demichelis Copyright (c) 2016-2024 Gaijin Games KFT Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ----------------------------------------------------- END OF COPYRIGHT ================================================ FILE: squirrel/compiler/arena.h ================================================ #pragma once #include "squtils.h" #include #include #include #include #include #include #define ALIGN_SIZE(len, align) (((len)+(align - 1)) & ~((align)-1)) #define ALIGN_SIZE_TO_WORD(len) ALIGN_SIZE(len, 0x8) class Arena { public: Arena(SQAllocContext alloc_ctx, const char *name, size_t chunkSize = 16<<10) : _alloc_ctx(alloc_ctx), _name(name), _chunks(NULL) , _chunkSize(chunkSize) { } Arena(const Arena&) = delete; Arena& operator=(const Arena&) = delete; ~Arena() { release(); } uint8_t *allocate(size_t size) { size = ALIGN_SIZE_TO_WORD(size); if (_chunks && size <= _chunks->left()) return _chunks->allocate(size); // Request doesn't fit in current chunk - allocate new size_t dataSize = (size > _chunkSize) ? size : _chunkSize; size_t totalSize = sizeof(Chunk) + dataSize; uint8_t *block = (uint8_t *)SQ_MALLOC(_alloc_ctx, totalSize); assert(block); uint8_t *data = block + sizeof(Chunk); _chunks = new(block) Chunk(_chunks, data, dataSize, totalSize); return _chunks->allocate(size); } bool tryExtend(void *ptr, size_t oldSize, size_t newSize) { if (!_chunks) return false; uint8_t *end = (uint8_t *)ptr + oldSize; if (end == _chunks->_ptr && newSize - oldSize <= _chunks->left()) { _chunks->_ptr += (newSize - oldSize); return true; } return false; } void release() { struct Chunk *ch = _chunks; while (ch) { struct Chunk *cur = ch; ch = cur->_next; SQ_FREE(_alloc_ctx, cur, cur->_totalSize); } _chunks = NULL; } private: struct Chunk { struct Chunk *_next; uint8_t *_start; uint8_t *_ptr; size_t _size; size_t _totalSize; // sizeof(Chunk) + _size, for SQ_FREE size_t allocated() const { return _ptr - _start; } size_t left() const { return _size - allocated(); } Chunk(struct Chunk *n, uint8_t *p, size_t s, size_t total) : _next(n), _start(p), _ptr(p), _size(s), _totalSize(total) { } uint8_t *allocate(size_t size) { assert(size <= left()); uint8_t *res = _ptr; _ptr += size; return res; } }; struct Chunk *_chunks; SQAllocContext _alloc_ctx; const char *_name; size_t _chunkSize; }; class ArenaObj { protected: ArenaObj() = default; ~ArenaObj() = default; private: void *operator new(size_t /*size*/) = delete; public: void *operator new(size_t size, Arena *arena) { return arena->allocate(size); } void operator delete(void * /*p*/, Arena * /*arena*/) { } void operator delete(void * /*p*/) { } }; template class ArenaVector { public: using size_type = uint32_t; ArenaVector(Arena *arena) : _arena(arena) , _vals(NULL) , _size(0) , _allocated(0) { } // Implement if needed ArenaVector(const ArenaVector&) = delete; ArenaVector& operator=(const ArenaVector&) = delete; ArenaVector(ArenaVector&& other) noexcept : _arena(other._arena) , _vals(other._vals) , _size(other._size) , _allocated(other._allocated) { other._vals = nullptr; other._size = 0; other._allocated = 0; } inline T &push_back(const T& val) { if (_allocated <= _size) _realloc(_size * 2); return *(new ((void *)&_vals[_size++]) T(val)); } inline void pop_back() { _size--; _vals[_size].~T(); } T& top() const { return _vals[_size - 1]; } inline size_type size() const { return _size; } bool empty() const { return (_size == 0); } inline T &back() const { return _vals[_size - 1]; } inline T& operator[](size_type pos) const { return _vals[pos]; } void insert(size_type pos, T v) { assert(pos <= _size); if (_allocated <= _size) _realloc(_size * 2); memmove(&_vals[pos + 1], &_vals[pos], (_size - pos) * sizeof(T)); new ((void *)&_vals[pos]) T(v); _size += 1; } typedef T* iterator; typedef const T* const_iterator; iterator begin() { return _vals; } const_iterator begin() const { return _vals; } iterator end() { return _vals + _size; } const_iterator end() const { return _vals + _size; } void reserve(size_type newSize) { if (newSize <= _allocated) return; _realloc(newSize); } private: void _realloc(size_type newsize) { newsize = (newsize > 0) ? newsize : 4; size_t newBytes = newsize * sizeof(T); if (_vals) { size_t oldBytes = ALIGN_SIZE_TO_WORD(_allocated * sizeof(T)); if (_arena->tryExtend(_vals, oldBytes, ALIGN_SIZE_TO_WORD(newBytes))) { _allocated = newsize; return; } T *newPtr = (T *)_arena->allocate(newBytes); memcpy(newPtr, _vals, _size * sizeof(T)); _vals = newPtr; } else { _vals = (T *)_arena->allocate(newBytes); } _allocated = newsize; } Arena *_arena; T *_vals; size_type _size; size_type _allocated; }; template class StdArenaAllocator { public: Arena *_arena; typedef size_t size_type; typedef T* pointer; typedef const T* const_pointer; typedef T value_type; pointer allocate(const size_type n) { return (pointer)_arena->allocate(n * sizeof(T)); } pointer allocate(const size_type n, const void *hint) { return allocate(n); } void deallocate(const_pointer p, const size_type n) {} template bool operator==(const StdArenaAllocator &other) const { return _arena == other._arena; } template bool operator!=(const StdArenaAllocator &other) const { return _arena != other._arena; } template StdArenaAllocator(const StdArenaAllocator &a) : StdArenaAllocator(a._arena) {} StdArenaAllocator(Arena *arena) : _arena(arena) { assert(arena); } ~StdArenaAllocator() {} }; template> struct ArenaMap : public std::map>> { typedef StdArenaAllocator> Allocator; ArenaMap(const Allocator &allocator) : std::map(allocator) {} }; template> struct ArenaSet : public std::set> { typedef StdArenaAllocator Allocator; ArenaSet(const Allocator &allocator) : std::set(allocator) {} }; template, typename KeyEq = std::equal_to> struct ArenaUnorderedSet : public std::unordered_set> { typedef StdArenaAllocator Allocator; ArenaUnorderedSet(const Allocator &allocator) : std::unordered_set(allocator) {} }; template, typename KeyEq = std::equal_to> struct ArenaUnorderedMap : public std::unordered_map>> { typedef StdArenaAllocator> Allocator; ArenaUnorderedMap(const Allocator &allocator) : std::unordered_map(allocator) {} }; ================================================ FILE: squirrel/compiler/ast.cpp ================================================ #include "ast.h" namespace SQCompilation { #define DEF_TREE_OP(arg) #arg const char* sq_tree_op_names[] = { TREE_OPS }; #undef DEF_TREE_OP void Node::visitChildren(Visitor *visitor) { switch (op()) { case TO_BLOCK: static_cast(this)->visitChildren(visitor); return; case TO_IF: static_cast(this)->visitChildren(visitor); return; case TO_WHILE: static_cast(this)->visitChildren(visitor); return; case TO_DOWHILE: static_cast(this)->visitChildren(visitor); return; case TO_FOR: static_cast(this)->visitChildren(visitor); return; case TO_FOREACH: static_cast(this)->visitChildren(visitor); return; case TO_SWITCH: static_cast(this)->visitChildren(visitor); return; case TO_RETURN: static_cast(this)->visitChildren(visitor); return; case TO_YIELD: static_cast(this)->visitChildren(visitor); return; case TO_THROW: static_cast(this)->visitChildren(visitor); return; case TO_TRY: static_cast(this)->visitChildren(visitor); return; case TO_BREAK: static_cast(this)->visitChildren(visitor); return; case TO_CONTINUE: static_cast(this)->visitChildren(visitor); return; case TO_EXPR_STMT: static_cast(this)->visitChildren(visitor); return; case TO_EMPTY: static_cast(this)->visitChildren(visitor); return; //case TO_STATEMENT_MARK: case TO_ID: static_cast(this)->visitChildren(visitor); return; case TO_COMMA: static_cast(this)->visitChildren(visitor); return; case TO_NULLC: case TO_ASSIGN: case TO_OROR: case TO_ANDAND: case TO_OR: case TO_XOR: case TO_AND: case TO_NE: case TO_EQ: case TO_3CMP: case TO_GE: case TO_GT: case TO_LE: case TO_LT: case TO_IN: case TO_INSTANCEOF: case TO_USHR: case TO_SHR: case TO_SHL: case TO_MUL: case TO_DIV: case TO_MOD: case TO_ADD: case TO_SUB: case TO_NEWSLOT: case TO_PLUSEQ: case TO_MINUSEQ: case TO_MULEQ: case TO_DIVEQ: case TO_MODEQ: static_cast(this)->visitChildren(visitor); return; case TO_NOT: case TO_BNOT: case TO_NEG: case TO_TYPEOF: case TO_RESUME: case TO_CLONE: case TO_PAREN: case TO_DELETE: case TO_STATIC_MEMO: case TO_INLINE_CONST: static_cast(this)->visitChildren(visitor); return; case TO_CODE_BLOCK_EXPR: static_cast(this)->visitChildren(visitor); return; case TO_LITERAL: static_cast(this)->visitChildren(visitor); return; case TO_BASE: static_cast(this)->visitChildren(visitor); return; case TO_ROOT_TABLE_ACCESS: static_cast(this)->visitChildren(visitor); return; case TO_INC: static_cast(this)->visitChildren(visitor); return; case TO_TABLE: static_cast(this)->visitChildren(visitor); return; case TO_CLASS: static_cast(this)->visitChildren(visitor); return; case TO_FUNCTION: static_cast(this)->visitChildren(visitor); return; case TO_ARRAY: static_cast(this)->visitChildren(visitor); return; case TO_GETFIELD: static_cast(this)->visitChildren(visitor); return; case TO_SETFIELD: static_cast(this)->visitChildren(visitor); return; case TO_GETSLOT: static_cast(this)->visitChildren(visitor); return; case TO_SETSLOT: static_cast(this)->visitChildren(visitor); return; case TO_CALL: static_cast(this)->visitChildren(visitor); return; case TO_TERNARY: static_cast(this)->visitChildren(visitor); return; //case TO_EXPR_MARK: case TO_VAR: static_cast(this)->visitChildren(visitor); return; case TO_PARAM: static_cast(this)->visitChildren(visitor); return; case TO_CONST: static_cast(this)->visitChildren(visitor); return; case TO_DECL_GROUP: static_cast(this)->visitChildren(visitor); return; case TO_DESTRUCTURE: static_cast(this)->visitChildren(visitor); return; case TO_ENUM: static_cast(this)->visitChildren(visitor); return; default: break; } } void Node::transformChildren(Transformer *transformer) { switch (op()) { case TO_BLOCK: static_cast(this)->transformChildren(transformer); return; case TO_IF: static_cast(this)->transformChildren(transformer); return; case TO_WHILE: static_cast(this)->transformChildren(transformer); return; case TO_DOWHILE: static_cast(this)->transformChildren(transformer); return; case TO_FOR: static_cast(this)->transformChildren(transformer); return; case TO_FOREACH: static_cast(this)->transformChildren(transformer); return; case TO_SWITCH: static_cast(this)->transformChildren(transformer); return; case TO_RETURN: static_cast(this)->transformChildren(transformer); return; case TO_YIELD: static_cast(this)->transformChildren(transformer); return; case TO_THROW: static_cast(this)->transformChildren(transformer); return; case TO_TRY: static_cast(this)->transformChildren(transformer); return; case TO_BREAK: static_cast(this)->transformChildren(transformer); return; case TO_CONTINUE: static_cast(this)->transformChildren(transformer); return; case TO_EXPR_STMT: static_cast(this)->transformChildren(transformer); return; case TO_EMPTY: static_cast(this)->transformChildren(transformer); return; //case TO_STATEMENT_MARK: case TO_ID: static_cast(this)->transformChildren(transformer); return; case TO_COMMA: static_cast(this)->transformChildren(transformer); return; case TO_NULLC: case TO_ASSIGN: case TO_OROR: case TO_ANDAND: case TO_OR: case TO_XOR: case TO_AND: case TO_NE: case TO_EQ: case TO_3CMP: case TO_GE: case TO_GT: case TO_LE: case TO_LT: case TO_IN: case TO_INSTANCEOF: case TO_USHR: case TO_SHR: case TO_SHL: case TO_MUL: case TO_DIV: case TO_MOD: case TO_ADD: case TO_SUB: case TO_NEWSLOT: case TO_PLUSEQ: case TO_MINUSEQ: case TO_MULEQ: case TO_DIVEQ: case TO_MODEQ: static_cast(this)->transformChildren(transformer); return; case TO_NOT: case TO_BNOT: case TO_NEG: case TO_TYPEOF: case TO_RESUME: case TO_CLONE: case TO_PAREN: case TO_DELETE: case TO_STATIC_MEMO: case TO_INLINE_CONST: static_cast(this)->transformChildren(transformer); return; case TO_CODE_BLOCK_EXPR: static_cast(this)->transformChildren(transformer); return; case TO_LITERAL: static_cast(this)->transformChildren(transformer); return; case TO_BASE: static_cast(this)->transformChildren(transformer); return; case TO_ROOT_TABLE_ACCESS: static_cast(this)->transformChildren(transformer); return; case TO_INC: static_cast(this)->transformChildren(transformer); return; case TO_TABLE: static_cast(this)->transformChildren(transformer); return; case TO_CLASS: static_cast(this)->transformChildren(transformer); return; case TO_FUNCTION: static_cast(this)->transformChildren(transformer); return; case TO_ARRAY: static_cast(this)->transformChildren(transformer); return; case TO_GETFIELD: static_cast(this)->transformChildren(transformer); return; case TO_SETFIELD: static_cast(this)->transformChildren(transformer); return; case TO_GETSLOT: static_cast(this)->transformChildren(transformer); return; case TO_SETSLOT: static_cast(this)->transformChildren(transformer); return; case TO_CALL: static_cast(this)->transformChildren(transformer); return; case TO_TERNARY: static_cast(this)->transformChildren(transformer); return; //case TO_EXPR_MARK: case TO_VAR: static_cast(this)->transformChildren(transformer); return; case TO_PARAM: static_cast(this)->transformChildren(transformer); return; case TO_CONST: static_cast(this)->transformChildren(transformer); return; case TO_DECL_GROUP: static_cast(this)->transformChildren(transformer); return; case TO_DESTRUCTURE: static_cast(this)->transformChildren(transformer); return; case TO_ENUM: static_cast(this)->transformChildren(transformer); return; default: break; } } void UnExpr::visitChildren(Visitor *visitor) { _arg->visit(visitor); } void UnExpr::transformChildren(Transformer *transformer) { _arg = _arg->transform(transformer)->asExpression(); } void CodeBlockExpr::visitChildren(Visitor *visitor) { _block->visit(visitor); } void CodeBlockExpr::transformChildren(Transformer *transformer) { _block = static_cast(_block->transform(transformer)); assert(_block->op() == TO_BLOCK); } void BinExpr::visitChildren(Visitor *visitor) { _lhs->visit(visitor); _rhs->visit(visitor); } void BinExpr::transformChildren(Transformer *transformer) { _lhs = _lhs->transform(transformer)->asExpression(); _rhs = _rhs->transform(transformer)->asExpression(); } void TerExpr::visitChildren(Visitor *visitor) { _a->visit(visitor); _b->visit(visitor); _c->visit(visitor); } void TerExpr::transformChildren(Transformer *transformer) { _a = _a->transform(transformer)->asExpression(); _b = _b->transform(transformer)->asExpression(); _c = _c->transform(transformer)->asExpression(); } void GetFieldExpr::visitChildren(Visitor *visitor) { receiver()->visit(visitor); } void GetFieldExpr::transformChildren(Transformer *transformer) { _receiver = receiver()->transform(transformer)->asExpression(); } void SetFieldExpr::visitChildren(Visitor *visitor) { receiver()->visit(visitor); value()->visit(visitor); } void SetFieldExpr::transformChildren(Transformer *transformer) { _receiver = receiver()->transform(transformer)->asExpression(); _value = value()->transform(transformer)->asExpression(); } void GetSlotExpr::visitChildren(Visitor *visitor) { receiver()->visit(visitor); key()->visit(visitor); } void GetSlotExpr::transformChildren(Transformer *transformer) { _receiver = receiver()->transform(transformer)->asExpression(); _key = key()->transform(transformer)->asExpression(); } void SetSlotExpr::visitChildren(Visitor *visitor) { receiver()->visit(visitor); key()->visit(visitor); value()->visit(visitor); } void SetSlotExpr::transformChildren(Transformer *transformer) { _receiver = receiver()->transform(transformer)->asExpression(); _key = key()->transform(transformer)->asExpression(); _val = value()->transform(transformer)->asExpression(); } void IncExpr::visitChildren(Visitor *visitor) { _arg->visit(visitor); } void IncExpr::transformChildren(Transformer *transformer) { _arg = _arg->transform(transformer)->asExpression(); } void CallExpr::visitChildren(Visitor *visitor) { _callee->visit(visitor); for (auto arg : arguments()) arg->visit(visitor); } void CallExpr::transformChildren(Transformer *transformer) { _callee = _callee->transform(transformer)->asExpression(); for (auto &arg : arguments()) arg = arg->transform(transformer)->asExpression(); } void ArrayExpr::visitChildren(Visitor *visitor) { for (auto init : initializers()) init->visit(visitor); } void ArrayExpr::transformChildren(Transformer *transformer) { for (auto &init : initializers()) init = init->transform(transformer)->asExpression(); } void CommaExpr::visitChildren(Visitor *visitor) { for (auto expr : expressions()) expr->visit(visitor); } void CommaExpr::transformChildren(Transformer *transformer) { for (auto &expr : expressions()) expr = expr->transform(transformer)->asExpression(); } void ValueDecl::visitChildren(Visitor *visitor) { if (_expr) _expr->visit(visitor); } void ValueDecl::transformChildren(Transformer *transformer) { if (_expr) { _expr = _expr->transform(transformer)->asExpression(); } } void TableExpr::visitChildren(Visitor *visitor) { for (auto &member : members()) { member.key->visit(visitor); member.value->visit(visitor); } } void TableExpr::transformChildren(Transformer *transformer) { for (auto &member : members()) { member.key = member.key->transform(transformer)->asExpression(); member.value = member.value->transform(transformer)->asExpression(); } } void ClassExpr::visitChildren(Visitor *visitor) { if (_key) _key->visit(visitor); if (_base) _base->visit(visitor); TableExpr::visitChildren(visitor); } void ClassExpr::transformChildren(Transformer *transformer) { if (_key) _key = _key->transform(transformer)->asExpression(); if (_base) _base = _base->transform(transformer)->asExpression(); TableExpr::transformChildren(transformer); } void FunctionExpr::visitChildren(Visitor *visitor) { for (auto param : parameters()) param->visit(visitor); for (auto param : parameters()) if (param->getDestructuring()) param->getDestructuring()->visit(visitor); body()->visit(visitor); } void FunctionExpr::transformChildren(Transformer *transformer) { for (auto ¶m : parameters()) { param = param->transform(transformer)->asDeclaration()->asParam(); } for (auto param : parameters()) if (param->getDestructuring()) param->setDestructuring(param->getDestructuring()->transform(transformer)->asDestructuringDecl()); setBody(body()->transform(transformer)->asStatement()->asBlock()); } void FunctionExpr::setBody(Block *body) { _body = body; body->setIsBody(); setSpanEnd(body->sourceSpan().end); } FunctionExpr *ClassExpr::findConstructor() const { for (const auto &m : members()) { if (m.value->op() == TO_FUNCTION) { FunctionExpr *f = m.value->asFunctionExpr(); if (f->name() && strcmp(f->name(), "constructor") == 0) return f; } } return nullptr; } void ConstDecl::visitChildren(Visitor *visitor) { _value->visit(visitor); } void ConstDecl::transformChildren(Transformer *transformer) { _value = _value->transform(transformer)->asExpression(); } void DeclGroup::visitChildren(Visitor *visitor) { for (auto decl : declarations()) decl->visit(visitor); } void DeclGroup::transformChildren(Transformer *transformer) { for (auto &decl : declarations()) decl = decl->transform(transformer)->asDeclaration()->asVarDecl(); } void DestructuringDecl::visitChildren(Visitor *visitor) { DeclGroup::visitChildren(visitor); _expr->visit(visitor); } void DestructuringDecl::transformChildren(Transformer *transformer) { DeclGroup::transformChildren(transformer); _expr = _expr->transform(transformer)->asExpression(); } void Block::visitChildren(Visitor *visitor) { for (auto stmt : statements()) stmt->visit(visitor); } void Block::transformChildren(Transformer *transformer) { for (auto &stmt : statements()) stmt = stmt->transform(transformer)->asStatement(); } void IfStatement::visitChildren(Visitor *visitor) { _cond->visit(visitor); _thenB->visit(visitor); if (_elseB) _elseB->visit(visitor); } void IfStatement::transformChildren(Transformer *transformer) { _cond = _cond->transform(transformer)->asExpression(); _thenB = _thenB->transform(transformer)->asStatement(); if (_elseB) _elseB = _elseB->transform(transformer)->asStatement(); } void LoopStatement::visitChildren(Visitor *visitor) { _body->visit(visitor); } void LoopStatement::transformChildren(Transformer *transformer) { _body = _body->transform(transformer)->asStatement(); } void WhileStatement::visitChildren(Visitor *visitor) { _cond->visit(visitor); LoopStatement::visitChildren(visitor); } void WhileStatement::transformChildren(Transformer *transformer) { _cond = _cond->transform(transformer)->asExpression(); LoopStatement::transformChildren(transformer); } void DoWhileStatement::visitChildren(Visitor *visitor) { LoopStatement::visitChildren(visitor); _cond->visit(visitor); } void DoWhileStatement::transformChildren(Transformer *transformer) { LoopStatement::transformChildren(transformer); _cond = _cond->transform(transformer)->asExpression(); } void ForStatement::visitChildren(Visitor *visitor) { if (_init) _init->visit(visitor); if (_cond) _cond->visit(visitor); if (_mod) _mod->visit(visitor); LoopStatement::visitChildren(visitor); } void ForStatement::transformChildren(Transformer *transformer) { if (_init) _init = _init->transform(transformer); if (_cond) _cond = _cond->transform(transformer)->asExpression(); if (_mod) _mod = _mod->transform(transformer)->asExpression(); LoopStatement::transformChildren(transformer); } void ForeachStatement::visitChildren(Visitor *visitor) { if (_idx) _idx->visit(visitor); if (_val) _val->visit(visitor); if (_container) _container->visit(visitor); LoopStatement::visitChildren(visitor); } void ForeachStatement::transformChildren(Transformer *transformer) { if (_idx) _idx = _idx->transform(transformer)->asDeclaration()->asVarDecl(); if (_val) _val = _val->transform(transformer)->asDeclaration()->asVarDecl(); if (_container) _container = _container->transform(transformer)->asExpression(); LoopStatement::transformChildren(transformer); } void SwitchStatement::visitChildren(Visitor *visitor) { _expr->visit(visitor); for (auto &c : cases()) { c.val->visit(visitor); c.stmt->visit(visitor); } if (_defaultCase.stmt) { _defaultCase.stmt->visit(visitor); } } void SwitchStatement::transformChildren(Transformer *transformer) { _expr = _expr->transform(transformer)->asExpression(); for (auto &c : cases()) { c.val = c.val->transform(transformer)->asExpression(); c.stmt = c.stmt->transform(transformer)->asStatement(); } if (_defaultCase.stmt) { _defaultCase.stmt = _defaultCase.stmt->transform(transformer)->asStatement(); } } void TryStatement::visitChildren(Visitor *visitor) { _tryStmt->visit(visitor); visitor->visitId(_exception); _catchStmt->visit(visitor); } void TryStatement::transformChildren(Transformer *transformer) { _tryStmt = _tryStmt->transform(transformer)->asStatement(); _exception = _exception->transform(transformer)->asId(); _catchStmt = _catchStmt->transform(transformer)->asStatement(); } void TerminateStatement::visitChildren(Visitor *visitor) { if (_arg) _arg->visit(visitor); } void TerminateStatement::transformChildren(Transformer *transformer) { if (_arg) _arg = _arg->transform(transformer)->asExpression(); } void ExprStatement::visitChildren(Visitor *visitor) { _expr->visit(visitor); } void ExprStatement::transformChildren(Transformer *transformer) { _expr = _expr->transform(transformer)->asExpression(); } const char* treeopStr(enum TreeOp op) { switch (op) { case TO_NULLC: return "??"; case TO_ASSIGN: return "="; case TO_OROR: return "||"; case TO_ANDAND: return "&&"; case TO_OR: return "|"; case TO_XOR: return "^"; case TO_AND: return "&"; case TO_NE: return "!="; case TO_EQ: return "=="; case TO_3CMP: return "<=>"; case TO_GE: return ">="; case TO_GT: return ">"; case TO_LE: return "<="; case TO_LT: return "<"; case TO_USHR: return ">>>"; case TO_SHR: return ">>"; case TO_SHL: return "<<"; case TO_MUL: return "*"; case TO_DIV: return "/"; case TO_MOD: return "%"; case TO_ADD: return "+"; case TO_SUB: case TO_NEG: return "-"; case TO_NOT: return "!"; case TO_BNOT: return "~"; case TO_NEWSLOT: return "<-"; case TO_PLUSEQ: return "+="; case TO_MINUSEQ: return "-="; case TO_MULEQ: return "*="; case TO_DIVEQ: return "/="; case TO_MODEQ: return "%="; default: return nullptr; } } }; ================================================ FILE: squirrel/compiler/ast.h ================================================ #pragma once #include #include "squirrel.h" #include "squtils.h" #include "arena.h" #include "sqobject.h" #include "sourceloc.h" // NOTE: There are some checkers that rely on the order of this list so re-arrange it carefully #define TREE_OPS \ DEF_TREE_OP(BLOCK), \ DEF_TREE_OP(IF), \ DEF_TREE_OP(WHILE), \ DEF_TREE_OP(DOWHILE), \ DEF_TREE_OP(FOR), \ DEF_TREE_OP(FOREACH), \ DEF_TREE_OP(SWITCH), \ DEF_TREE_OP(RETURN), \ DEF_TREE_OP(YIELD), \ DEF_TREE_OP(THROW), \ DEF_TREE_OP(TRY), \ DEF_TREE_OP(BREAK), \ DEF_TREE_OP(CONTINUE), \ DEF_TREE_OP(EXPR_STMT), \ DEF_TREE_OP(EMPTY), \ DEF_TREE_OP(DIRECTIVE), \ DEF_TREE_OP(IMPORT), \ \ DEF_TREE_OP(STATEMENT_MARK), \ \ DEF_TREE_OP(ID), \ DEF_TREE_OP(COMMA), \ DEF_TREE_OP(NULLC), \ DEF_TREE_OP(ASSIGN), \ DEF_TREE_OP(OROR), \ DEF_TREE_OP(ANDAND), \ DEF_TREE_OP(OR), \ DEF_TREE_OP(XOR), \ DEF_TREE_OP(AND), \ DEF_TREE_OP(NE), \ DEF_TREE_OP(EQ), \ DEF_TREE_OP(3CMP), \ DEF_TREE_OP(GE), \ DEF_TREE_OP(GT), \ DEF_TREE_OP(LE), \ DEF_TREE_OP(LT), \ DEF_TREE_OP(IN), \ DEF_TREE_OP(INSTANCEOF), \ DEF_TREE_OP(USHR), \ DEF_TREE_OP(SHR), \ DEF_TREE_OP(SHL), \ DEF_TREE_OP(MUL), \ DEF_TREE_OP(DIV), \ DEF_TREE_OP(MOD), \ DEF_TREE_OP(ADD), \ DEF_TREE_OP(SUB), \ DEF_TREE_OP(NEWSLOT), \ DEF_TREE_OP(PLUSEQ), \ DEF_TREE_OP(MINUSEQ), \ DEF_TREE_OP(MULEQ), \ DEF_TREE_OP(DIVEQ), \ DEF_TREE_OP(MODEQ), \ DEF_TREE_OP(NOT), \ DEF_TREE_OP(BNOT), \ DEF_TREE_OP(NEG), \ DEF_TREE_OP(TYPEOF), \ DEF_TREE_OP(STATIC_MEMO), \ DEF_TREE_OP(INLINE_CONST), \ DEF_TREE_OP(RESUME), \ DEF_TREE_OP(CLONE), \ DEF_TREE_OP(PAREN), \ DEF_TREE_OP(CODE_BLOCK_EXPR), \ DEF_TREE_OP(DELETE), \ DEF_TREE_OP(LITERAL), \ DEF_TREE_OP(BASE), \ DEF_TREE_OP(ROOT_TABLE_ACCESS), \ DEF_TREE_OP(INC), \ DEF_TREE_OP(ARRAY), \ DEF_TREE_OP(TABLE), \ DEF_TREE_OP(CLASS), \ DEF_TREE_OP(FUNCTION), \ DEF_TREE_OP(GETFIELD), \ DEF_TREE_OP(SETFIELD), \ DEF_TREE_OP(GETSLOT), \ DEF_TREE_OP(SETSLOT), \ DEF_TREE_OP(CALL), \ DEF_TREE_OP(TERNARY), \ DEF_TREE_OP(EXTERNAL_VALUE), \ \ DEF_TREE_OP(EXPR_MARK), \ \ DEF_TREE_OP(VAR), \ DEF_TREE_OP(PARAM), \ DEF_TREE_OP(CONST), \ DEF_TREE_OP(DECL_GROUP), \ DEF_TREE_OP(DESTRUCTURE), \ DEF_TREE_OP(ENUM), \ DEF_TREE_OP(DECLARATION_MARK), \ enum TreeOp { #define DEF_TREE_OP(arg) TO_##arg TREE_OPS #undef DEF_TREE_OP }; namespace SQCompilation { extern const char* sq_tree_op_names[]; class Visitor; class Transformer; class Expr; class Statement; class Decl; class DestructuringDecl; class Id; class GetFieldExpr; class GetSlotExpr; class Node : public ArenaObj { protected: Node(enum TreeOp op, SourceSpan span) : _op(op), _span(span) {} public: ~Node() {} enum TreeOp op() const { return _op; } template void visit(V *visitor); template Node *transform(T *transformer); void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); bool isDeclaration() const { return TO_EXPR_MARK < op() && op() < TO_DECLARATION_MARK; } bool isStatement() const { return _op < TO_STATEMENT_MARK; } bool isExpression() const { return TO_STATEMENT_MARK < _op && _op < TO_EXPR_MARK; } bool isStatementOrDeclaration() const { return isStatement() || isDeclaration(); } inline Expr *asExpression() const { assert(isExpression()); return (Expr *)(this); } inline Statement *asStatement() const { assert(isStatement() || isDeclaration()); return (Statement *)(this); } inline Decl *asDeclaration() const { assert(isDeclaration()); return (Decl *)(this); } Id *asId() { assert(_op == TO_ID); return (Id*)this; } const Id *asId() const { assert(_op == TO_ID); return (const Id*)this; } GetFieldExpr *asGetField() const { assert(_op == TO_GETFIELD); return (GetFieldExpr*)this; } GetSlotExpr *asGetSlot() const { assert(_op == TO_GETSLOT); return (GetSlotExpr*)this; } DestructuringDecl *asDestructuringDecl() const { assert(_op == TO_DESTRUCTURE); return (DestructuringDecl*)this; } SourceSpan sourceSpan() const { return _span; } // Only for incrementally-built nodes void setSpanEnd(SourceLoc end) { _span.end = end; } // Convenience accessors int lineStart() const { return _span.start.line; } int lineEnd() const { return _span.end.line; } int columnStart() const { return _span.start.column; } int columnEnd() const { return _span.end.column; } int textWidth() const { return _span.textWidth(); } private: enum TreeOp _op; SourceSpan _span; }; struct DocObject { DocObject() : docString(nullptr) {} void setDocString(const char *doc_string) { docString = doc_string; } const char *getDocString() const { return docString; } bool isEmpty() const { return docString == nullptr; } // TODO: set/get DocTable (use SQObjectPtr ?) private: const char *docString; }; class AccessExpr; class LiteralExpr; class BinExpr; class CallExpr; class ExternalValueExpr; class TableExpr; class ClassExpr; class FunctionExpr; class Expr : public Node { protected: Expr(enum TreeOp op, SourceSpan span) : Node(op, span), _typeMask(~0u), _typeInferred(false) {} unsigned _typeMask; bool _typeInferred; public: unsigned getTypeMask() const { return _typeMask; } void setTypeMask(unsigned m) { _typeMask = m; } bool isTypeInferred() const { return _typeInferred; } void setTypeInferred() { _typeInferred = true; } bool isAccessExpr() const { return TO_GETFIELD <= op() && op() <= TO_SETSLOT; } AccessExpr *asAccessExpr() const { assert(isAccessExpr()); return (AccessExpr*)this; } LiteralExpr *asLiteral() const { assert(op() == TO_LITERAL); return (LiteralExpr *)this; } BinExpr *asBinExpr() const { assert(TO_NULLC <= op() && op() <= TO_MODEQ); return (BinExpr *)this; } CallExpr *asCallExpr() const { assert(op() == TO_CALL); return (CallExpr *)this; } ExternalValueExpr *asExternal() const { assert(op() == TO_EXTERNAL_VALUE); return (ExternalValueExpr *)this; } TableExpr *asTableExpr() const { assert(op() == TO_TABLE || op() == TO_CLASS); return (TableExpr *)this; } ClassExpr *asClassExpr() const { assert(op() == TO_CLASS); return (ClassExpr *)this; } FunctionExpr *asFunctionExpr() const { assert(op() == TO_FUNCTION); return (FunctionExpr *)this; } }; class Id : public Expr { public: Id(SourceSpan span, const char *name) : Expr(TO_ID, span), _name(name) {} void visitChildren(Visitor *visitor) {} void transformChildren(Transformer *transformer) {} const char *name() const { return _name; } private: const char *_name; }; class UnExpr : public Expr { public: UnExpr(enum TreeOp op, SourceLoc opStart, Expr *arg) : Expr(op, {opStart, arg->sourceSpan().end}), _arg(arg) {} void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); Expr *argument() const { return _arg; } private: Expr *_arg; }; class BinExpr : public Expr { public: BinExpr(enum TreeOp op, Expr *lhs, Expr *rhs) : Expr(op, SourceSpan::merge(lhs->sourceSpan(), rhs->sourceSpan())), _lhs(lhs), _rhs(rhs) {} void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); Expr *lhs() const { return _lhs; } Expr *rhs() const { return _rhs; } private: Expr *_lhs; Expr *_rhs; }; class TerExpr : public Expr { public: TerExpr(Expr *a, Expr *b, Expr *c) : Expr(TO_TERNARY, SourceSpan::merge(a->sourceSpan(), c->sourceSpan())), _a(a), _b(b), _c(c) {} void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); Expr *a() const { return _a; } Expr *b() const { return _b; } Expr *c() const { return _c; } private: Expr *_a; Expr *_b; Expr *_c; }; class FieldAccessExpr; class AccessExpr : public Expr { protected: AccessExpr(enum TreeOp op, SourceSpan span, Expr *receiver, bool nullable) : Expr(op, span), _receiver(receiver), _nullable(nullable) {} public: bool isFieldAccessExpr() const { return op() == TO_GETFIELD || op() == TO_SETFIELD; } FieldAccessExpr *asFieldAccessExpr() const { assert(isFieldAccessExpr()); return (FieldAccessExpr *)this; } bool isNullable() const { return _nullable; } Expr *receiver() const { return _receiver; } protected: Expr *_receiver; bool _nullable; }; class FieldAccessExpr : public AccessExpr { protected: FieldAccessExpr(enum TreeOp op, SourceSpan span, Expr *receiver, const char *field, bool nullable) : AccessExpr(op, span, receiver, nullable), _fieldName(field) {} public: bool canBeLiteral(bool typeMethod) const { return receiver()->op() != TO_BASE && !isNullable() && !typeMethod; } const char *fieldName() const { return _fieldName; } private: const char *_fieldName; }; class GetFieldExpr : public FieldAccessExpr { bool _isTypeMethod; public: // Constructor computes span from receiver start to end of field name GetFieldExpr(Expr *receiver, const char *field, bool nullable, bool is_type_method, SourceLoc end) : FieldAccessExpr(TO_GETFIELD, {receiver->sourceSpan().start, end}, receiver, field, nullable) , _isTypeMethod(is_type_method) {} void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); bool isTypeMethod() const { return _isTypeMethod; } }; class SetFieldExpr : public FieldAccessExpr { public: // Constructor computes span from receiver start to value end SetFieldExpr(Expr *receiver, const char *field, Expr *value, bool nullable) : FieldAccessExpr(TO_SETFIELD, {receiver->sourceSpan().start, value->sourceSpan().end}, receiver, field, nullable) , _value(value) {} void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); Expr *value() const { return _value; } protected: Expr *_value; }; class SlotAccessExpr : public AccessExpr { protected: SlotAccessExpr(enum TreeOp op, SourceSpan span, Expr *receiver, Expr *key, bool nullable) : AccessExpr(op, span, receiver, nullable), _key(key) {} public: Expr *key() const { return _key; } protected: Expr *_key; }; class GetSlotExpr : public SlotAccessExpr { public: GetSlotExpr(Expr *receiver, Expr *key, bool nullable, SourceLoc end) : SlotAccessExpr(TO_GETSLOT, {receiver->sourceSpan().start, end}, receiver, key, nullable) {} void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); }; class SetSlotExpr : public SlotAccessExpr { public: SetSlotExpr(Expr *receiver, Expr *key, Expr *val, bool nullable) : SlotAccessExpr(TO_SETSLOT, {receiver->sourceSpan().start, val->sourceSpan().end}, receiver, key, nullable) , _val(val) {} void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); Expr *value() const { return _val; } private: Expr *_val; }; class BaseExpr : public Expr { public: BaseExpr(SourceSpan span) : Expr(TO_BASE, span) {} void visitChildren(Visitor *visitor) {} void transformChildren(Transformer *transformer) {} }; class RootTableAccessExpr : public Expr { public: RootTableAccessExpr(SourceSpan span) : Expr(TO_ROOT_TABLE_ACCESS, span) {} void visitChildren(Visitor *visitor) {} void transformChildren(Transformer *transformer) {} }; enum LiteralKind { LK_STRING, LK_INT, LK_FLOAT, LK_BOOL, LK_NULL }; class LiteralExpr : public Expr { public: LiteralExpr(SourceSpan span) : Expr(TO_LITERAL, span), _kind(LK_NULL) { _v.raw = 0; } LiteralExpr(SourceSpan span, const char *s) : Expr(TO_LITERAL, span), _kind(LK_STRING) { _v.s = s; } LiteralExpr(SourceSpan span, SQFloat f) : Expr(TO_LITERAL, span), _kind(LK_FLOAT) { _v.f = f; } LiteralExpr(SourceSpan span, SQInteger i) : Expr(TO_LITERAL, span), _kind(LK_INT) { _v.i = i; } LiteralExpr(SourceSpan span, bool b) : Expr(TO_LITERAL, span), _kind(LK_BOOL) { _v.b = b; } void visitChildren(Visitor *visitor) {} void transformChildren(Transformer *transformer) {} enum LiteralKind kind() const { return _kind; } SQFloat f() const { assert(_kind == LK_FLOAT); return _v.f; } SQInteger i() const { assert(_kind == LK_INT); return _v.i; } bool b() const { assert(_kind == LK_BOOL); return _v.b; } const char *s() const { assert(_kind == LK_STRING); return _v.s; } void *null() const { assert(_kind == LK_NULL); return nullptr; } SQUnsignedInteger raw() const { return _v.raw; } private: enum LiteralKind _kind; union { const char *s; SQInteger i; SQFloat f; bool b; SQUnsignedInteger raw; } _v; }; // Used in the analyzer for external bindings class ExternalValueExpr : public Expr { public: ExternalValueExpr(const SQObject &from) : Expr(TO_EXTERNAL_VALUE, SourceSpan::invalid()), _value(from) {} ExternalValueExpr(const SQObject &from, SourceSpan span) : Expr(TO_EXTERNAL_VALUE, span), _value(from) {} void visitChildren(Visitor *visitor) {} void transformChildren(Transformer *transformer) {} SQObjectPtr& value() { return _value; } const SQObjectPtr& value() const { return _value; } private: SQObjectPtr _value; // To release this, ~ExternalValueExpr() dtor is called explicitly }; enum IncForm { IF_PREFIX, IF_POSTFIX }; class IncExpr : public Expr { public: // Constructor computes span based on form // For prefix: opLoc is the start of the operator, span is {opLoc, arg->span().end} // For postfix: opLoc is the end of the operator, span is {arg->span().start, opLoc} IncExpr(Expr *arg, int diff, enum IncForm form, SourceLoc opLoc) : Expr(TO_INC, form == IF_PREFIX ? SourceSpan{opLoc, arg->sourceSpan().end} : SourceSpan{arg->sourceSpan().start, opLoc}) , _arg(arg), _diff(diff), _form(form) {} void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); enum IncForm form() const { return _form; } int diff() const { return _diff; } Expr *argument() const { return _arg; } private: Expr *_arg; int _diff; enum IncForm _form; }; class CallExpr : public Expr { public: // For incremental building - call setSpanEnd() after adding arguments CallExpr(Arena *arena, Expr *callee, bool nullable) : Expr(TO_CALL, {callee->sourceSpan().start, SourceLoc::invalid()}) , _callee(callee), _args(arena), _nullable(nullable) {} // For cases where end is known at construction time CallExpr(Arena *arena, Expr *callee, bool nullable, SourceLoc end) : Expr(TO_CALL, {callee->sourceSpan().start, end}) , _callee(callee), _args(arena), _nullable(nullable) {} void addArgument(Expr *arg) { _args.push_back(arg); } void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); bool isNullable() const { return _nullable; } Expr *callee() const { return _callee; } const ArenaVector &arguments() const { return _args; } ArenaVector &arguments() { return _args; } private: Expr *_callee; ArenaVector _args; bool _nullable; }; class ArrayExpr : public Expr { public: // Incremental building - call setSpanEnd() after adding values ArrayExpr(Arena *arena, SourceLoc start) : Expr(TO_ARRAY, {start, SourceLoc::invalid()}), _inits(arena) {} void addValue(Expr *v) { _inits.push_back(v); } void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); const ArenaVector &initializers() const { return _inits; } ArenaVector &initializers() { return _inits; } private: ArenaVector _inits; }; class CommaExpr : public Expr { public: CommaExpr(Arena *arena, ArenaVector exprs) : Expr(TO_COMMA, computeSpan(exprs)), _exprs(std::move(exprs)) {} void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); const ArenaVector &expressions() const { return _exprs; } ArenaVector &expressions() { return _exprs; } private: static SourceSpan computeSpan(const ArenaVector &exprs) { if (exprs.empty()) return SourceSpan::invalid(); return SourceSpan::merge(exprs[0]->sourceSpan(), exprs.back()->sourceSpan()); } ArenaVector _exprs; }; class Block; class Statement : public Node { protected: Statement(enum TreeOp op, SourceSpan span) : Node(op, span) {} public: inline Block *asBlock() const { assert(op() == TO_BLOCK); return (Block *)(this); } }; class DirectiveStmt : public Statement { public: DirectiveStmt(SourceSpan span) : Statement(TO_DIRECTIVE, span) {} void visitChildren(Visitor *visitor) {} void transformChildren(Transformer *transformer) {} uint32_t setFlags = 0, clearFlags = 0; bool applyToDefault = false; }; class ImportStmt : public Statement { public: // Incremental building - call setSpanEnd() after parsing ImportStmt(Arena *arena_, SourceLoc start, const char *module_name, const char *module_alias) : Statement(TO_IMPORT, {start, SourceLoc::invalid()}) , arena(arena_), moduleName(module_name), moduleAlias(module_alias), slots(arena_) {} void visitChildren(Visitor *visitor) {} void transformChildren(Transformer *transformer) {} public: Arena *arena; const char *moduleName; const char *moduleAlias; int nameCol = 0; int aliasCol = 0; ArenaVector slots; }; class ParamDecl; class VarDecl; class Decl : public Statement { protected: Decl(enum TreeOp op, SourceSpan span) : Statement(op, span), typeMask(~0u) {} unsigned typeMask; public: ParamDecl *asParam() const { assert(op() == TO_PARAM); return (ParamDecl *)(this); } VarDecl *asVarDecl() const { assert(op() == TO_VAR); return (VarDecl *)(this); } void setTypeMask(unsigned typeMask_) { typeMask = typeMask_; } unsigned getTypeMask() const { return typeMask; } }; class ValueDecl : public Decl { protected: ValueDecl(enum TreeOp op, SourceSpan span, const char *name, Expr *expr) : Decl(op, span), _name(name), _expr(expr) {} public: void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); Expr *expression() const { return _expr; } const char *name() const { return _name; } private: const char *_name; Expr *_expr; }; class DestructuringDecl; class ParamDecl : public ValueDecl { bool _isVararg; DestructuringDecl *_destructuring; public: ParamDecl(SourceSpan nameSpan, const char *name, Expr *defaultVal) : ValueDecl(TO_PARAM, {nameSpan.start, defaultVal ? defaultVal->sourceSpan().end : nameSpan.end}, name, defaultVal) , _isVararg(false) , _destructuring(nullptr) {} bool hasDefaultValue() const { return expression() != NULL; } Expr *defaultValue() const { return expression(); } void setVararg() { _isVararg = true; }; bool isVararg() const { return _isVararg; } void setDestructuring(DestructuringDecl *destructuring) { _destructuring = destructuring; } DestructuringDecl *getDestructuring() const { return _destructuring; } }; class VarDecl : public ValueDecl { Id *_nameId; public: VarDecl(SourceLoc start, Id *nameId, Expr *init, bool assignable, bool destructured = false) : ValueDecl(TO_VAR, {start, init ? init->sourceSpan().end : nameId->sourceSpan().end}, nameId->name(), init) , _nameId(nameId), _assignable(assignable), _destructured(destructured) {} Expr *initializer() const { return expression(); } // Get the Id node (for diagnostics - points to identifier) Id *nameId() const { return _nameId; } bool isAssignable() const { return _assignable; } bool isDestructured() const { return _destructured; } private: bool _assignable; bool _destructured; }; enum TableMemberFlags : unsigned { TMF_STATIC = (1U << 0), TMF_DYNAMIC_KEY = (1U << 1), TMF_JSON = (1U << 2) }; struct TableMember { Expr *key; Expr *value; unsigned flags; bool isStatic() const { return (flags & TMF_STATIC) != 0; } bool isDynamicKey() const { return (flags & TMF_DYNAMIC_KEY) != 0; } bool isJson() const { return (flags & TMF_JSON) != 0; } }; class TableExpr : public Expr { public: // Incremental building - call setSpanEnd() after adding members TableExpr(Arena *arena, SourceLoc start) : Expr(TO_TABLE, {start, SourceLoc::invalid()}), _members(arena) {} void addMember(Expr *key, Expr *value, unsigned keys = 0) { _members.push_back({ key, value, keys }); } void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); ArenaVector &members() { return _members; } const ArenaVector &members() const { return _members; } DocObject docObject; protected: TableExpr(Arena *arena, enum TreeOp op, SourceLoc start) : Expr(op, {start, SourceLoc::invalid()}), _members(arena) {} private: ArenaVector _members; }; class ClassExpr : public TableExpr { public: // Incremental building - span starts at class keyword, call setSpanEnd() after body ClassExpr(Arena *arena, SourceLoc classKeywordStart, Expr *key, Expr *base) : TableExpr(arena, TO_CLASS, classKeywordStart), _key(key), _base(base) {} void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); Expr *classBase() const { return _base; } Expr* classKey() const { return _key; } void setClassBase(Expr *b) { _base = b; } void setClassKey(Expr *k) { _key = k; } FunctionExpr *findConstructor() const; private: Expr *_key; Expr *_base; }; class Block; class FunctionExpr : public Expr { Id *_nameId; // Name identifier (for diagnostics) protected: // Incremental building - call setSpanEnd() after body is set FunctionExpr(enum TreeOp op, Arena *arena, SourceLoc start, const char *name, Id *nameId = nullptr) : Expr(op, {start, SourceLoc::invalid()}), _arena(arena), _parameters(arena), _name(name), _nameId(nameId), _vararg(false), _body(NULL), _lambda(false), _pure(false), _nodiscard(false), _sourcename(NULL), _hoistingLevel(0), _resultTypeMask(~0u) {} public: FunctionExpr(Arena *arena, SourceLoc start, Id *nameId) : Expr(TO_FUNCTION, {start, SourceLoc::invalid()}), _arena(arena), _parameters(arena), _name(nameId->name()), _nameId(nameId), _vararg(false), _body(NULL), _lambda(false), _pure(false), _nodiscard(false), _sourcename(NULL), _hoistingLevel(0), _resultTypeMask(~0u) {} FunctionExpr(Arena *arena, SourceLoc start, const char *name) : Expr(TO_FUNCTION, {start, SourceLoc::invalid()}), _arena(arena), _parameters(arena), _name(name), _nameId(nullptr), _vararg(false), _body(NULL), _lambda(false), _pure(false), _nodiscard(false), _sourcename(NULL), _hoistingLevel(0), _resultTypeMask(~0u) {} void addParameter(SourceSpan nameSpan, const char *name, Expr *defaultVal = NULL) { _parameters.push_back(new (_arena) ParamDecl(nameSpan, name, defaultVal)); } ArenaVector ¶meters() { return _parameters; } const ArenaVector ¶meters() const { return _parameters; } void setVararg(bool v) { _vararg = v; } void setBody(Block *body); void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); void setName(const char *newName) { _name = newName; } const char *name() const { return _name; } Id *nameId() const { return _nameId; } bool isVararg() const { return _vararg; } Block *body() const { return _body; } void setSourceName(const char *sn) { _sourcename = sn; } const char *sourceName() const { return _sourcename; } bool isLambda() const { return _lambda; } void setLambda(bool v) { _lambda = v; } void setPure(bool v) { _pure = v; } bool isPure() const { return _pure; } void setNodiscard(bool v) { _nodiscard = v; } bool isNodiscard() const { return _nodiscard; } int hoistingLevel() const { return _hoistingLevel; } void hoistBy(int level) { _hoistingLevel += level; } unsigned getResultTypeMask() const { return _resultTypeMask; } void setResultTypeMask(unsigned resultTypeMask) { _resultTypeMask = resultTypeMask; } DocObject docObject; private: Arena *_arena; const char *_name; ArenaVector _parameters; Block * _body; unsigned _resultTypeMask; bool _vararg; bool _lambda; bool _pure; bool _nodiscard; const char *_sourcename; int _hoistingLevel; }; struct EnumConst { const char *id; LiteralExpr *val; }; class EnumDecl : public Decl { public: // Incremental building - call setSpanEnd() after constants are added EnumDecl(Arena *arena, SourceLoc start, const char *id, bool global) : Decl(TO_ENUM, {start, SourceLoc::invalid()}), _id(id), _consts(arena), _global(global) {} void addConst(const char *id, LiteralExpr *val) { _consts.push_back({ id, val }); } ArenaVector &consts() { return _consts; } const ArenaVector &consts() const { return _consts; } void visitChildren(Visitor *visitor) {} void transformChildren(Transformer *transformer) {} const char *name() const { return _id; } bool isGlobal() const { return _global; } private: ArenaVector _consts; const char *_id; bool _global; }; class ConstDecl : public Decl { public: ConstDecl(SourceLoc start, const char *id, Expr *value, bool global) : Decl(TO_CONST, {start, value->sourceSpan().end}), _id(id), _value(value), _global(global) {} void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); const char *name() const { return _id; } Expr *value() const { return _value; } bool isGlobal() const { return _global; } private: const char *_id; Expr *_value; bool _global; }; class DeclGroup : public Decl { protected: // Incremental building - span start is keyword, end is set when last decl is added DeclGroup(Arena *arena, enum TreeOp op, SourceLoc start) : Decl(op, {start, SourceLoc::invalid()}), _decls(arena) {} public: DeclGroup(Arena *arena, SourceLoc start) : Decl(TO_DECL_GROUP, {start, SourceLoc::invalid()}), _decls(arena) {} void addDeclaration(VarDecl *d) { _decls.push_back(d); // Update span end to include the new declaration setSpanEnd(d->sourceSpan().end); } void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); ArenaVector &declarations() { return _decls; } const ArenaVector &declarations() const { return _decls; } private: ArenaVector _decls; }; enum DestructuringType { DT_TABLE, DT_ARRAY }; class DestructuringDecl : public DeclGroup { public: DestructuringDecl(Arena *arena, SourceLoc start, enum DestructuringType dt) : DeclGroup(arena, TO_DESTRUCTURE, start), _dt_type(dt), _expr(NULL) {} void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); void setExpression(Expr *expr) { _expr = expr; setSpanEnd(_expr->sourceSpan().end); } Expr *initExpression() const { return _expr; } void setType(enum DestructuringType t) { _dt_type = t; } enum DestructuringType type() const { return _dt_type; } private: Expr *_expr; enum DestructuringType _dt_type; }; class Block : public Statement { public: // Incremental building - call setSpanEnd() after statements are added Block(Arena *arena, SourceLoc start, bool is_root = false) : Statement(TO_BLOCK, {start, SourceLoc::invalid()}) , _statements(arena), _is_root(is_root), _is_body(false), _is_expr_block(false) {} void addStatement(Statement *stmt) { assert(stmt); _statements.push_back(stmt); } ArenaVector &statements() { return _statements; } const ArenaVector &statements() const { return _statements; } void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); bool isRoot() const { return _is_root; } bool isBody() const { return _is_body; } void setIsBody() { _is_body = true; } bool isExprBlock() { return _is_expr_block; } void setIsExprBlock() { _is_expr_block = true; } private: ArenaVector _statements; bool _is_root; bool _is_body; bool _is_expr_block; }; class RootBlock : public Block { public: RootBlock(Arena *arena, SourceLoc start) : Block(arena, start, true) {} }; class CodeBlockExpr : public Expr { public: CodeBlockExpr(Block *block): Expr(TO_CODE_BLOCK_EXPR, block->sourceSpan()), _block(block) {} void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); Block *block() const { return _block; } private: Block *_block; }; class IfStatement : public Statement { public: IfStatement(SourceLoc start, Expr *cond, Statement *thenB, Statement *elseB) : Statement(TO_IF, {start, (elseB ? elseB : thenB)->sourceSpan().end}) , _cond(cond), _thenB(thenB), _elseB(elseB) {} void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); Expr *condition() const { return _cond; } Statement *thenBranch() const { return _thenB; } Statement *elseBranch() const { return _elseB; } private: Expr *_cond; Statement *_thenB; Statement *_elseB; }; class LoopStatement : public Statement { protected: // Constructor computes span from keyword start to body end LoopStatement(enum TreeOp op, SourceLoc start, Statement *body) : Statement(op, {start, body->sourceSpan().end}), _body(body) {} // For DoWhile which needs a different span LoopStatement(enum TreeOp op, SourceSpan span, Statement *body) : Statement(op, span), _body(body) {} public: void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); Statement *body() const { return _body; } private: Statement *_body; }; class WhileStatement : public LoopStatement { public: WhileStatement(SourceLoc start, Expr *cond, Statement *body) : LoopStatement(TO_WHILE, start, body), _cond(cond) {} void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); Expr *condition() const { return _cond; } private: Expr *_cond; }; class DoWhileStatement : public LoopStatement { public: DoWhileStatement(SourceLoc start, Statement *body, Expr *cond, SourceLoc end) : LoopStatement(TO_DOWHILE, {start, end}, body), _cond(cond) {} void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); Expr *condition() const { return _cond; } private: Expr *_cond; }; class ForStatement : public LoopStatement { public: ForStatement(SourceLoc start, Node *init, Expr *cond, Expr *mod, Statement *body) : LoopStatement(TO_FOR, start, body), _init(init), _cond(cond), _mod(mod) {} void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); Node *initializer() const { return _init; } Expr *condition() const { return _cond; } Expr *modifier() const { return _mod; } private: Node *_init; Expr *_cond; Expr *_mod; }; class ForeachStatement : public LoopStatement { public: ForeachStatement(SourceLoc start, VarDecl *idx, VarDecl *val, Expr *container, Statement *body) : LoopStatement(TO_FOREACH, start, body), _idx(idx), _val(val), _container(container) {} void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); Expr *container() const { return _container; } VarDecl *idx() const { return _idx; } VarDecl *val() const { return _val; } private: VarDecl *_idx; VarDecl *_val; Expr *_container; }; struct SwitchCase { Expr *val; Statement *stmt; }; class SwitchStatement : public Statement { public: // Incremental building - call setSpanEnd() after cases are added SwitchStatement(SourceLoc start, Arena *arena, Expr *expr) : Statement(TO_SWITCH, {start, SourceLoc::invalid()}), _expr(expr), _cases(arena), _defaultCase() {} void addCases(Expr *val, Statement *stmt) { _cases.push_back({ val, stmt }); } void addDefault(Statement *stmt) { assert(_defaultCase.stmt == NULL); _defaultCase.stmt = stmt; } ArenaVector &cases() { return _cases; } const ArenaVector &cases() const { return _cases; } Expr *expression() const { return _expr; } const SwitchCase &defaultCase() const { return _defaultCase; } void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); private: Expr *_expr; ArenaVector _cases; SwitchCase _defaultCase; }; class TryStatement : public Statement { public: TryStatement(SourceLoc start, Statement *t, Id *exc, Statement *c) : Statement(TO_TRY, {start, c->sourceSpan().end}), _tryStmt(t), _exception(exc), _catchStmt(c) {} void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); Statement *tryStatement() const { return _tryStmt; } Id *exceptionId() const { return _exception; } Statement *catchStatement() const { return _catchStmt; } private: Statement *_tryStmt; Id *_exception; Statement *_catchStmt; }; class TerminateStatement : public Statement { protected: TerminateStatement(enum TreeOp op, SourceSpan keywordSpan, Expr *arg) : Statement(op, arg ? SourceSpan{keywordSpan.start, arg->sourceSpan().end} : keywordSpan) , _arg(arg) {} public: void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); Expr *argument() const { return _arg; } private: Expr *_arg; }; class ReturnStatement : public TerminateStatement { public: ReturnStatement(SourceSpan keywordSpan, Expr *arg) : TerminateStatement(TO_RETURN, keywordSpan, arg), _isLambda(false) {} void setIsLambda() { _isLambda = true; } bool isLambdaReturn() const { return _isLambda; } private: bool _isLambda; }; class YieldStatement : public TerminateStatement { public: YieldStatement(SourceSpan keywordSpan, Expr *arg) : TerminateStatement(TO_YIELD, keywordSpan, arg) {} }; class ThrowStatement : public TerminateStatement { public: ThrowStatement(SourceSpan keywordSpan, Expr *arg) : TerminateStatement(TO_THROW, keywordSpan, arg) { assert(arg); } }; class JumpStatement : public Statement { protected: JumpStatement(enum TreeOp op, SourceSpan span) : Statement(op, span) {} public: void visitChildren(Visitor *visitor) {} void transformChildren(Transformer *transformer) {} }; class BreakStatement : public JumpStatement { public: BreakStatement(SourceSpan span, Statement *breakTarget) : JumpStatement(TO_BREAK, span), _target(breakTarget) {} private: Statement *_target; // Currently not used }; class ContinueStatement : public JumpStatement { public: ContinueStatement(SourceSpan span, LoopStatement *target) : JumpStatement(TO_CONTINUE, span), _target(target) {} private: LoopStatement *_target; // Currently not used }; class ExprStatement : public Statement { public: ExprStatement(Expr *expr) : Statement(TO_EXPR_STMT, expr->sourceSpan()), _expr(expr) {} void visitChildren(Visitor *visitor); void transformChildren(Transformer *transformer); Expr *expression() const { return _expr; } private: Expr *_expr; }; class EmptyStatement : public Statement { public: EmptyStatement(SourceSpan span) : Statement(TO_EMPTY, span) {} void visitChildren(Visitor *visitor) {} void transformChildren(Transformer *transformer) {} }; const char* treeopStr(enum TreeOp op); class Visitor { protected: Visitor() {} public: virtual ~Visitor() {} virtual void visitNode(Node *node) { node->visitChildren(this); } virtual void visitExpr(Expr *expr) { visitNode(expr); } virtual void visitUnExpr(UnExpr *expr) { visitExpr(expr); } virtual void visitCodeBlockExpr(CodeBlockExpr *expr) { visitExpr(expr); } virtual void visitBinExpr(BinExpr *expr) { visitExpr(expr); } virtual void visitTerExpr(TerExpr *expr) { visitExpr(expr); } virtual void visitCallExpr(CallExpr *expr) { visitExpr(expr); } virtual void visitId(Id *id) { visitExpr(id); } virtual void visitAccessExpr(AccessExpr *expr) { visitExpr(expr); } virtual void visitGetFieldExpr(GetFieldExpr *expr) { visitAccessExpr(expr); } virtual void visitSetFieldExpr(SetFieldExpr *expr) { visitAccessExpr(expr); } virtual void visitGetSlotExpr(GetSlotExpr *expr) { visitAccessExpr(expr); } virtual void visitSetSlotExpr(SetSlotExpr *expr) { visitAccessExpr(expr); } virtual void visitBaseExpr(BaseExpr *expr) { visitExpr(expr); } virtual void visitRootTableAccessExpr(RootTableAccessExpr *expr) { visitExpr(expr); } virtual void visitLiteralExpr(LiteralExpr *expr) { visitExpr(expr); } virtual void visitIncExpr(IncExpr *expr) { visitExpr(expr); } virtual void visitArrayExpr(ArrayExpr *expr) { visitExpr(expr); } virtual void visitTableExpr(TableExpr *tbl) { visitExpr(tbl); } virtual void visitClassExpr(ClassExpr *cls) { visitTableExpr(cls); } virtual void visitFunctionExpr(FunctionExpr *f) { visitExpr(f); } virtual void visitCommaExpr(CommaExpr *expr) { visitExpr(expr); } virtual void visitExternalValueExpr(ExternalValueExpr *expr) { visitExpr(expr); } virtual void visitStmt(Statement *stmt) { visitNode(stmt); } virtual void visitBlock(Block *block) { visitStmt(block); } virtual void visitIfStatement(IfStatement *ifstmt) { visitStmt(ifstmt); } virtual void visitLoopStatement(LoopStatement *loop) { visitStmt(loop); } virtual void visitWhileStatement(WhileStatement *loop) { visitLoopStatement(loop); } virtual void visitDoWhileStatement(DoWhileStatement *loop) { visitLoopStatement(loop); } virtual void visitForStatement(ForStatement *loop) { visitLoopStatement(loop); } virtual void visitForeachStatement(ForeachStatement *loop) { visitLoopStatement(loop); } virtual void visitSwitchStatement(SwitchStatement *swtch) { visitStmt(swtch); } virtual void visitTryStatement(TryStatement *tr) { visitStmt(tr); } virtual void visitTerminateStatement(TerminateStatement *term) { visitStmt(term); } virtual void visitReturnStatement(ReturnStatement *ret) { visitTerminateStatement(ret); } virtual void visitYieldStatement(YieldStatement *yld) { visitTerminateStatement(yld); } virtual void visitThrowStatement(ThrowStatement *thr) { visitTerminateStatement(thr); } virtual void visitJumpStatement(JumpStatement *jmp) { visitStmt(jmp); } virtual void visitBreakStatement(BreakStatement *jmp) { visitJumpStatement(jmp); } virtual void visitContinueStatement(ContinueStatement *jmp) { visitJumpStatement(jmp); } virtual void visitExprStatement(ExprStatement *estmt) { visitStmt(estmt); } virtual void visitEmptyStatement(EmptyStatement *empty) { visitStmt(empty); } virtual void visitDecl(Decl *decl) { visitStmt(decl); } virtual void visitValueDecl(ValueDecl *decl) { visitDecl(decl); } virtual void visitVarDecl(VarDecl *decl) { visitValueDecl(decl); } virtual void visitParamDecl(ParamDecl *decl) { visitValueDecl(decl); } virtual void visitConstDecl(ConstDecl *cnst) { visitDecl(cnst); } virtual void visitEnumDecl(EnumDecl *enm) { visitDecl(enm); } virtual void visitDeclGroup(DeclGroup *grp) { visitDecl(grp); } virtual void visitDestructuringDecl(DestructuringDecl *destruct) { visitDecl(destruct); } virtual void visitDirectiveStatement(DirectiveStmt *dir) { visitStmt(dir); } virtual void visitImportStatement(ImportStmt *import) { visitStmt(import); } }; class Transformer { protected: Transformer() {} public: virtual ~Transformer() {} virtual Node* transformNode(Node *node) { node->transformChildren(this); return node; } virtual Node *transformExpr(Expr *expr) { return transformNode(expr); } virtual Node *transformUnExpr(UnExpr *expr) { return transformExpr(expr); } virtual Node *transformCodeBlockExpr(CodeBlockExpr *expr) { return transformExpr(expr); } virtual Node *transformBinExpr(BinExpr *expr) { return transformExpr(expr); } virtual Node *transformTerExpr(TerExpr *expr) { return transformExpr(expr); } virtual Node *transformCallExpr(CallExpr *expr) { return transformExpr(expr); } virtual Node *transformId(Id *id) { return transformExpr(id); } virtual Node *transformGetFieldExpr(GetFieldExpr *expr) { return transformExpr(expr); } virtual Node *transformSetFieldExpr(SetFieldExpr *expr) { return transformExpr(expr); } virtual Node *transformGetSlotExpr(GetSlotExpr *expr) { return transformExpr(expr); } virtual Node *transformSetSlotExpr(SetSlotExpr *expr) { return transformExpr(expr); } virtual Node *transformBaseExpr(BaseExpr *expr) { return transformExpr(expr); } virtual Node *transformRootTableAccessExpr(RootTableAccessExpr *expr) { return transformExpr(expr); } virtual Node *transformLiteralExpr(LiteralExpr *expr) { return transformExpr(expr); } virtual Node *transformIncExpr(IncExpr *expr) { return transformExpr(expr); } virtual Node *transformArrayExpr(ArrayExpr *expr) { return transformExpr(expr); } virtual Node *transformTableExpr(TableExpr *tbl) { return transformExpr(tbl); } virtual Node *transformClassExpr(ClassExpr *cls) { return transformTableExpr(cls); } virtual Node *transformFunctionExpr(FunctionExpr *f) { return transformExpr(f); } virtual Node *transformCommaExpr(CommaExpr *expr) { return transformExpr(expr); } virtual Node *transformExternalValueExpr(ExternalValueExpr *expr) { return transformExpr(expr); } virtual Node *transformStmt(Statement *stmt) { return transformNode(stmt); } virtual Node *transformBlock(Block *block) { return transformStmt(block); } virtual Node *transformIfStatement(IfStatement *ifstmt) { return transformStmt(ifstmt); } virtual Node *transformLoopStatement(LoopStatement *loop) { return transformStmt(loop); } virtual Node *transformWhileStatement(WhileStatement *loop) { return transformLoopStatement(loop); } virtual Node *transformDoWhileStatement(DoWhileStatement *loop) { return transformLoopStatement(loop); } virtual Node *transformForStatement(ForStatement *loop) { return transformLoopStatement(loop); } virtual Node *transformForeachStatement(ForeachStatement *loop) { return transformLoopStatement(loop); } virtual Node *transformSwitchStatement(SwitchStatement *swtch) { return transformStmt(swtch); } virtual Node *transformTryStatement(TryStatement *tr) { return transformStmt(tr); } virtual Node *transformTerminateStatement(TerminateStatement *term) { return transformStmt(term); } virtual Node *transformReturnStatement(ReturnStatement *ret) { return transformTerminateStatement(ret); } virtual Node *transformYieldStatement(YieldStatement *yld) { return transformTerminateStatement(yld); } virtual Node *transformThrowStatement(ThrowStatement *thr) { return transformTerminateStatement(thr); } virtual Node *transformJumpStatement(JumpStatement *jmp) { return transformStmt(jmp); } virtual Node *transformBreakStatement(BreakStatement *jmp) { return transformJumpStatement(jmp); } virtual Node *transformContinueStatement(ContinueStatement *jmp) { return transformJumpStatement(jmp); } virtual Node *transformExprStatement(ExprStatement *estmt) { return transformStmt(estmt); } virtual Node *transformEmptyStatement(EmptyStatement *empty) { return transformStmt(empty); } virtual Node *transformDecl(Decl *decl) { return transformStmt(decl); } virtual Node *transformValueDecl(ValueDecl *decl) { return transformDecl(decl); } virtual Node *transformVarDecl(VarDecl *decl) { return transformValueDecl(decl); } virtual Node *transformParamDecl(ParamDecl *decl) { return transformValueDecl(decl); } virtual Node *transformConstDecl(ConstDecl *cnst) { return transformDecl(cnst); } virtual Node *transformEnumDecl(EnumDecl *enm) { return transformDecl(enm); } virtual Node *transformDeclGroup(DeclGroup *grp) { return transformDecl(grp); } virtual Node *transformDestructuringDecl(DestructuringDecl *destruct) { return transformDecl(destruct); } }; template void Node::visit(V *visitor) { switch (op()) { case TO_BLOCK: visitor->visitBlock(static_cast(this)); return; case TO_IF: visitor->visitIfStatement(static_cast(this)); return; case TO_WHILE: visitor->visitWhileStatement(static_cast(this)); return; case TO_DOWHILE: visitor->visitDoWhileStatement(static_cast(this)); return; case TO_FOR: visitor->visitForStatement(static_cast(this)); return; case TO_FOREACH: visitor->visitForeachStatement(static_cast(this)); return; case TO_SWITCH: visitor->visitSwitchStatement(static_cast(this)); return; case TO_RETURN: visitor->visitReturnStatement(static_cast(this)); return; case TO_YIELD: visitor->visitYieldStatement(static_cast(this)); return; case TO_THROW: visitor->visitThrowStatement(static_cast(this)); return; case TO_TRY: visitor->visitTryStatement(static_cast(this)); return; case TO_BREAK: visitor->visitBreakStatement(static_cast(this)); return; case TO_CONTINUE: visitor->visitContinueStatement(static_cast(this)); return; case TO_EXPR_STMT: visitor->visitExprStatement(static_cast(this)); return; case TO_EMPTY: visitor->visitEmptyStatement(static_cast(this)); return; //case TO_STATEMENT_MARK: case TO_ID: visitor->visitId(static_cast(this)); return; case TO_COMMA: visitor->visitCommaExpr(static_cast(this)); return; case TO_NULLC: case TO_ASSIGN: case TO_OROR: case TO_ANDAND: case TO_OR: case TO_XOR: case TO_AND: case TO_NE: case TO_EQ: case TO_3CMP: case TO_GE: case TO_GT: case TO_LE: case TO_LT: case TO_IN: case TO_INSTANCEOF: case TO_USHR: case TO_SHR: case TO_SHL: case TO_MUL: case TO_DIV: case TO_MOD: case TO_ADD: case TO_SUB: case TO_NEWSLOT: case TO_PLUSEQ: case TO_MINUSEQ: case TO_MULEQ: case TO_DIVEQ: case TO_MODEQ: visitor->visitBinExpr(static_cast(this)); return; case TO_NOT: case TO_BNOT: case TO_NEG: case TO_TYPEOF: case TO_RESUME: case TO_CLONE: case TO_PAREN: case TO_DELETE: case TO_STATIC_MEMO: case TO_INLINE_CONST: visitor->visitUnExpr(static_cast(this)); return; case TO_CODE_BLOCK_EXPR: visitor->visitCodeBlockExpr(static_cast(this)); return; case TO_LITERAL: visitor->visitLiteralExpr(static_cast(this)); return; case TO_BASE: visitor->visitBaseExpr(static_cast(this)); return; case TO_ROOT_TABLE_ACCESS: visitor->visitRootTableAccessExpr(static_cast(this)); return; case TO_INC: visitor->visitIncExpr(static_cast(this)); return; case TO_ARRAY: visitor->visitArrayExpr(static_cast(this)); return; case TO_TABLE: visitor->visitTableExpr(static_cast(this)); return; case TO_CLASS: visitor->visitClassExpr(static_cast(this)); return; case TO_FUNCTION: visitor->visitFunctionExpr(static_cast(this)); return; case TO_GETFIELD: visitor->visitGetFieldExpr(static_cast(this)); return; case TO_SETFIELD: visitor->visitSetFieldExpr(static_cast(this)); return; case TO_GETSLOT: visitor->visitGetSlotExpr(static_cast(this)); return; case TO_SETSLOT: visitor->visitSetSlotExpr(static_cast(this)); return; case TO_CALL: visitor->visitCallExpr(static_cast(this)); return; case TO_TERNARY: visitor->visitTerExpr(static_cast(this)); return; case TO_EXTERNAL_VALUE: visitor->visitExternalValueExpr(static_cast(this)); return; //case TO_EXPR_MARK: case TO_VAR: visitor->visitVarDecl(static_cast(this)); return; case TO_PARAM: visitor->visitParamDecl(static_cast(this)); return; case TO_CONST: visitor->visitConstDecl(static_cast(this)); return; case TO_DECL_GROUP: visitor->visitDeclGroup(static_cast(this)); return; case TO_DESTRUCTURE: visitor->visitDestructuringDecl(static_cast(this)); return; case TO_ENUM: visitor->visitEnumDecl(static_cast(this)); return; case TO_DIRECTIVE: visitor->visitDirectiveStatement(static_cast(this)); return; case TO_IMPORT: visitor->visitImportStatement(static_cast(this)); return; default: break; } } template Node *Node::transform(T *transformer) { switch (op()) { case TO_BLOCK: return transformer->transformBlock(static_cast(this)); case TO_IF: return transformer->transformIfStatement(static_cast(this)); case TO_WHILE: return transformer->transformWhileStatement(static_cast(this)); case TO_DOWHILE: return transformer->transformDoWhileStatement(static_cast(this)); case TO_FOR: return transformer->transformForStatement(static_cast(this)); case TO_FOREACH: return transformer->transformForeachStatement(static_cast(this)); case TO_SWITCH: return transformer->transformSwitchStatement(static_cast(this)); case TO_RETURN: return transformer->transformReturnStatement(static_cast(this)); case TO_YIELD: return transformer->transformYieldStatement(static_cast(this)); case TO_THROW: return transformer->transformThrowStatement(static_cast(this)); case TO_TRY: return transformer->transformTryStatement(static_cast(this)); case TO_BREAK: return transformer->transformBreakStatement(static_cast(this)); case TO_CONTINUE: return transformer->transformContinueStatement(static_cast(this)); case TO_EXPR_STMT: return transformer->transformExprStatement(static_cast(this)); case TO_EMPTY: return transformer->transformEmptyStatement(static_cast(this)); //case TO_STATEMENT_MARK: case TO_ID: return transformer->transformId(static_cast(this)); case TO_COMMA: return transformer->transformCommaExpr(static_cast(this)); case TO_NULLC: case TO_ASSIGN: case TO_OROR: case TO_ANDAND: case TO_OR: case TO_XOR: case TO_AND: case TO_NE: case TO_EQ: case TO_3CMP: case TO_GE: case TO_GT: case TO_LE: case TO_LT: case TO_IN: case TO_INSTANCEOF: case TO_USHR: case TO_SHR: case TO_SHL: case TO_MUL: case TO_DIV: case TO_MOD: case TO_ADD: case TO_SUB: case TO_NEWSLOT: case TO_PLUSEQ: case TO_MINUSEQ: case TO_MULEQ: case TO_DIVEQ: case TO_MODEQ: return transformer->transformBinExpr(static_cast(this)); case TO_NOT: case TO_BNOT: case TO_NEG: case TO_TYPEOF: case TO_RESUME: case TO_CLONE: case TO_PAREN: case TO_DELETE: case TO_STATIC_MEMO: case TO_INLINE_CONST: return transformer->transformUnExpr(static_cast(this)); case TO_CODE_BLOCK_EXPR: return transformer->transformCodeBlockExpr(static_cast(this)); case TO_LITERAL: return transformer->transformLiteralExpr(static_cast(this)); case TO_BASE: return transformer->transformBaseExpr(static_cast(this)); case TO_ROOT_TABLE_ACCESS: return transformer->transformRootTableAccessExpr(static_cast(this)); case TO_INC: return transformer->transformIncExpr(static_cast(this)); case TO_ARRAY: return transformer->transformArrayExpr(static_cast(this)); case TO_TABLE: return transformer->transformTableExpr(static_cast(this)); case TO_CLASS: return transformer->transformClassExpr(static_cast(this)); case TO_FUNCTION: return transformer->transformFunctionExpr(static_cast(this)); case TO_GETFIELD: return transformer->transformGetFieldExpr(static_cast(this)); case TO_SETFIELD: return transformer->transformSetFieldExpr(static_cast(this)); case TO_GETSLOT: return transformer->transformGetSlotExpr(static_cast(this)); case TO_SETSLOT: return transformer->transformSetSlotExpr(static_cast(this)); case TO_CALL: return transformer->transformCallExpr(static_cast(this)); case TO_TERNARY: return transformer->transformTerExpr(static_cast(this)); case TO_EXTERNAL_VALUE: return transformer->transformExternalValueExpr(static_cast(this)); //case TO_EXPR_MARK: case TO_VAR: return transformer->transformVarDecl(static_cast(this)); case TO_PARAM: return transformer->transformParamDecl(static_cast(this)); case TO_CONST: return transformer->transformConstDecl(static_cast(this)); case TO_DECL_GROUP: return transformer->transformDeclGroup(static_cast(this)); case TO_DESTRUCTURE: return transformer->transformDestructuringDecl(static_cast(this)); case TO_ENUM: return transformer->transformEnumDecl(static_cast(this)); case TO_DIRECTIVE: return this; //-V1037 case TO_IMPORT: return this; //-V1037 default: assert(0 && "Unknown tree type"); return this; } } } // namespace SQCompilation ================================================ FILE: squirrel/compiler/codegen.cpp ================================================ #include "sqpcheader.h" #ifndef NO_COMPILER #include "opcodes.h" #include "sqstring.h" #include "sqfuncproto.h" #include "sqfuncstate.h" #include "codegen.h" #include "constgen.h" #include "sqvm.h" #include "optimizer.h" #include "sqtable.h" #include "sqarray.h" #include "sqclass.h" #include "sqclosure.h" #define GLOBAL_OPTIMIZATION_SWITCH true /* false - turn off broken optimizer */ #define STATIC_MEMO_CONST_SCORE_THRESHOLD 10 #define STATIC_MEMO_ENABLED 1 #ifndef SQ_LINE_INFO_IN_STRUCTURES # define SQ_LINE_INFO_IN_STRUCTURES 1 #endif #define BEGIN_SCOPE() SQScope __oldscope__ = _scope; \ _scope.outers = _fs->_outers; \ _scope.stacksize = _fs->GetStackSize(); \ _scopedconsts.push_back(); #define RESOLVE_OUTERS() if(_fs->GetStackSize() != _fs->_blockstacksizes.top()) { \ if(_fs->CountOuters(_fs->_blockstacksizes.top())) { \ _fs->AddInstruction(_OP_CLOSE,0,_fs->_blockstacksizes.top()); \ } \ } #define END_SCOPE_NO_CLOSE() { if(_fs->GetStackSize() != _scope.stacksize) { \ _fs->SetStackSize(_scope.stacksize); \ } \ _scope = __oldscope__; \ assert(!_scopedconsts.empty()); \ _scopedconsts.pop_back(); \ } #define END_SCOPE() { SQInteger oldouters = _fs->_outers;\ if(_fs->GetStackSize() != _scope.stacksize) { \ _fs->SetStackSize(_scope.stacksize); \ if(oldouters != _fs->_outers) { \ _fs->AddInstruction(_OP_CLOSE,0,_scope.stacksize); \ } \ } \ _scope = __oldscope__; \ _scopedconsts.pop_back(); \ } #define BEGIN_BREAKABLE_BLOCK() SQInteger __nbreaks__=_fs->_unresolvedbreaks.size(); \ SQInteger __ncontinues__=_fs->_unresolvedcontinues.size(); \ _fs->_breaktargets.push_back(0);_fs->_continuetargets.push_back(0); \ _fs->_blockstacksizes.push_back(_scope.stacksize); #define END_BREAKABLE_BLOCK(continue_target) {__nbreaks__=_fs->_unresolvedbreaks.size()-__nbreaks__; \ __ncontinues__=_fs->_unresolvedcontinues.size()-__ncontinues__; \ if(__ncontinues__>0)ResolveContinues(_fs,__ncontinues__,continue_target); \ if(__nbreaks__>0)ResolveBreaks(_fs,__nbreaks__); \ _fs->_breaktargets.pop_back();_fs->_continuetargets.pop_back(); \ _fs->_blockstacksizes.pop_back(); } namespace SQCompilation { CodeGenVisitor::CodeGenVisitor(Arena *arena, const HSQOBJECT *bindings, SQVM *vm, const char *sourceName, SQCompilationContext &ctx) : Visitor(), _fs(NULL), _childFs(NULL), _ctx(ctx), _scopedconsts(_ss(vm)->_alloc_ctx), _sourceName(sourceName), _vm(vm), _resolve_mode(ExprChainResolveMode::Value), _inside_static_memo(false), _visit_arrays_and_tables(false), _variable_node(nullptr), _arena(arena), _scope() { _complexity_level = 0; if (bindings) { assert(sq_type(*bindings) == OT_TABLE || sq_type(*bindings) == OT_NULL); if (sq_type(*bindings) == OT_TABLE) { _scopedconsts.push_back(SQObjectPtr(*bindings)); } } } void CodeGenVisitor::reportDiagnostic(Node *n, int32_t id, ...) { va_list vargs; va_start(vargs, id); _ctx.vreportDiagnostic((enum DiagnosticsId)id, n->lineStart(), n->columnStart(), n->textWidth(), vargs); va_end(vargs); } bool CodeGenVisitor::generate(RootBlock *root, SQObjectPtr &out) { SQFuncState funcstate(_ss(_vm), NULL, _ctx); try { _fs = &funcstate; _childFs = NULL; _fs->_name = SQString::Create(_ss(_vm), "__main__"); _fs->AddParameter(_fs->CreateString("this"), _RT_NULL); _fs->AddParameter(_fs->CreateString("vargv"), _RT_ARRAY); _fs->_varparams = true; _fs->_sourcename = SQString::Create(_ss(_vm), _sourceName); SQInteger stacksize = _fs->GetStackSize(); root->visit(this); _fs->SetStackSize(stacksize); _fs->AddLineInfos(root->lineEnd(), true, true); _fs->AddInstruction(_OP_RETURN, 0xFF); if (GLOBAL_OPTIMIZATION_SWITCH && !(_fs->lang_features & LF_DISABLE_OPTIMIZER)) { SQOptimizer opt(funcstate); opt.optimize(); _fs->CheckForPurity(); } _fs->SetStackSize(0); out = _fs->BuildProto(); _fs = NULL; return true; } catch (CompilerError &) { return false; } } void CodeGenVisitor::CheckDuplicateLocalIdentifier(Node *n, SQObject name, const char *desc, bool ignore_global_consts) { SQCompiletimeVarInfo varInfo; if (_fs->GetLocalVariable(name, varInfo) >= 0) reportDiagnostic(n, DiagnosticsId::DI_CONFLICTS_WITH, desc, _string(name)->_val, "existing local variable"); if (_string(name) == _string(_fs->_name)) reportDiagnostic(n, DiagnosticsId::DI_CONFLICTS_WITH, desc, _stringval(name), "function name"); SQObjectPtr constant; if (ignore_global_consts ? IsLocalConstant(name, constant) : IsConstant(name, constant)) reportDiagnostic(n, DiagnosticsId::DI_CONFLICTS_WITH, desc, _stringval(name), "existing constant/enum/import"); } static bool compareLiterals(LiteralExpr *a, LiteralExpr *b) { if (a->kind() != b->kind()) return false; if (a->kind() == LK_STRING) { return strcmp(a->s(), b->s()) == 0; } return a->raw() == b->raw(); } #ifdef _SQ64 # define SQ_UINT_FMT PRIu64 #else # define SQ_UINT_FMT PRIuPTR #endif // _SQ64 bool CodeGenVisitor::CheckMemberUniqueness(ArenaVector &vec, Expr *obj) { if (obj->op() != TO_LITERAL && obj->op() != TO_ID) return true; for (SQUnsignedInteger i = 0, n = vec.size(); i < n; ++i) { Expr *vecobj = vec[i]; if (vecobj->op() == TO_ID && obj->op() == TO_ID) { if (strcmp(vecobj->asId()->name(), obj->asId()->name()) == 0) { reportDiagnostic(obj, DiagnosticsId::DI_DUPLICATE_KEY, obj->asId()->name()); return false; } continue; } if (vecobj->op() == TO_LITERAL && obj->op() == TO_LITERAL) { LiteralExpr *a = (LiteralExpr*)vecobj; LiteralExpr *b = (LiteralExpr*)obj; if (compareLiterals(a, b)) { if (a->kind() == LK_STRING) { reportDiagnostic(obj, DiagnosticsId::DI_DUPLICATE_KEY, a->s()); } else { char b[32] = { 0 }; snprintf(b, sizeof b, "%" SQ_UINT_FMT, a->raw()); reportDiagnostic(obj, DiagnosticsId::DI_DUPLICATE_KEY, b); } return false; } continue; } } vec.push_back(obj); return true; } void CodeGenVisitor::EmitLoadConstInt(SQInteger value, SQInteger target) { if (target < 0) { target = _fs->PushTarget(); } if (value <= INT_MAX && value > INT_MIN) { //does it fit in 32 bits? _fs->AddInstruction(_OP_LOADINT, target, value); } else { _fs->AddInstruction(_OP_LOAD, target, _fs->GetNumericConstant(value)); } } void CodeGenVisitor::EmitLoadConstFloat(SQFloat value, SQInteger target) { if (target < 0) { target = _fs->PushTarget(); } if (sizeof(SQFloat) == sizeof(SQInt32)) { _fs->AddInstruction(_OP_LOADFLOAT, target, *((SQInt32 *)&value)); } else { _fs->AddInstruction(_OP_LOAD, target, _fs->GetNumericConstant(value)); } } void CodeGenVisitor::ResolveBreaks(SQFuncState *funcstate, SQInteger ntoresolve) { while (ntoresolve > 0) { SQInteger pos = funcstate->_unresolvedbreaks.back(); funcstate->_unresolvedbreaks.pop_back(); //set the jmp instruction funcstate->SetInstructionParams(pos, 0, funcstate->GetCurrentPos() - pos, 0); ntoresolve--; } } void CodeGenVisitor::ResolveContinues(SQFuncState *funcstate, SQInteger ntoresolve, SQInteger targetpos) { while (ntoresolve > 0) { SQInteger pos = funcstate->_unresolvedcontinues.back(); funcstate->_unresolvedcontinues.pop_back(); //set the jmp instruction funcstate->SetInstructionParams(pos, 0, targetpos - pos, 0); ntoresolve--; } } void CodeGenVisitor::EmitDerefOp(SQOpcode op) { SQInteger val = _fs->PopTarget(); SQInteger key = _fs->PopTarget(); SQInteger src = _fs->PopTarget(); _fs->AddInstruction(op, _fs->PushTarget(), src, key, val); } void CodeGenVisitor::EmitCheckType(int target, uint32_t type_mask) { #if SQ_RUNTIME_TYPE_CHECK if (type_mask != ~0u && target < 256) _fs->AddInstruction(_OP_CHECK_TYPE, target, type_mask); #else (void)target; (void)type_mask; #endif } void CodeGenVisitor::visitBlock(Block *block) { addLineNumber(block); BEGIN_SCOPE(); ArenaVector &statements = block->statements(); for (auto stmt : statements) { stmt->visit(this); _fs->SnoozeOpt(); } if (block->isBody() || block->isRoot()) { END_SCOPE_NO_CLOSE(); } else { END_SCOPE(); } } void CodeGenVisitor::visitIfStatement(IfStatement *ifStmt) { addLineNumber(ifStmt); BEGIN_SCOPE(); visitForValueMaybeStaticMemo(ifStmt->condition()); _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); SQInteger jnepos = _fs->GetCurrentPos(); ifStmt->thenBranch()->visit(this); SQInteger endifblock = _fs->GetCurrentPos(); if (ifStmt->elseBranch()) { _fs->AddInstruction(_OP_JMP); SQInteger jmppos = _fs->GetCurrentPos(); ifStmt->elseBranch()->visit(this); _fs->SetInstructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos); } _fs->SetInstructionParam(jnepos, 1, endifblock - jnepos + (ifStmt->elseBranch() ? 1 : 0)); END_SCOPE(); } void CodeGenVisitor::visitWhileStatement(WhileStatement *whileLoop) { addLineNumber(whileLoop); _complexity_level++; BEGIN_SCOPE(); { SQInteger jmppos = _fs->GetCurrentPos(); visitForValue(whileLoop->condition()); BEGIN_BREAKABLE_BLOCK(); _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); SQInteger jzpos = _fs->GetCurrentPos(); BEGIN_SCOPE(); whileLoop->body()->visit(this); END_SCOPE(); _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1); _fs->SetInstructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos); END_BREAKABLE_BLOCK(jmppos); } END_SCOPE(); _complexity_level--; } void CodeGenVisitor::visitDoWhileStatement(DoWhileStatement *doWhileLoop) { addLineNumber(doWhileLoop); _complexity_level++; BEGIN_SCOPE(); { SQInteger jmptrg = _fs->GetCurrentPos(); BEGIN_BREAKABLE_BLOCK(); BEGIN_SCOPE(); doWhileLoop->body()->visit(this); END_SCOPE(); SQInteger continuetrg = _fs->GetCurrentPos(); visitForValue(doWhileLoop->condition()); _fs->AddInstruction(_OP_JZ, _fs->PopTarget(), 1); _fs->AddInstruction(_OP_JMP, 0, jmptrg - _fs->GetCurrentPos() - 1); END_BREAKABLE_BLOCK(continuetrg); } END_SCOPE(); _complexity_level--; } void CodeGenVisitor::visitForStatement(ForStatement *forLoop) { addLineNumber(forLoop); _complexity_level++; BEGIN_SCOPE(); if (forLoop->initializer()) { Node *init = forLoop->initializer(); visitForValue(init); if (init->isExpression()) { _fs->PopTarget(); } } _fs->SnoozeOpt(); SQInteger jzpos = -1; if (forLoop->condition()) { _fs->SnoozeOpt(); visitForValue(forLoop->condition()); _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); jzpos = _fs->GetCurrentPos(); } SQInteger jmppos = _fs->GetCurrentPos(); SQInteger expstart = _fs->GetCurrentPos() + 1; if (forLoop->modifier()) { _fs->SnoozeOpt(); visitForValue(forLoop->modifier()); _fs->PopTarget(); } SQInteger expend = _fs->GetCurrentPos(); SQInteger expsize = (expend - expstart) + 1; ArenaVector exp(_arena); if (expsize > 0) { for (SQInteger i = 0; i < expsize; i++) exp.push_back(_fs->GetInstruction(expstart + i)); _fs->PopInstructions(expsize); } _fs->SnoozeOpt(); BEGIN_BREAKABLE_BLOCK(); forLoop->body()->visit(this); SQInteger continuetrg = _fs->GetCurrentPos(); if (expsize > 0) { for (SQInteger i = 0; i < expsize; i++) _fs->AddInstruction(exp[i]); } if (forLoop->condition()) { _fs->SnoozeOpt(); visitForValue(forLoop->condition()); const int cmpPos = _fs->GetCurrentPos(); _fs->AddInstruction(_OP_JZ, _fs->PopTarget(), jmppos - cmpPos, 1); if (cmpPos != _fs->GetCurrentPos()) // instruction was optimized _fs->SetInstructionParam(_fs->GetCurrentPos(), 1, jmppos - cmpPos - 1); } else _fs->AddInstruction(_OP_JMP, 0, jmppos - _fs->GetCurrentPos() - 1, 0); if (jzpos > 0) _fs->SetInstructionParam(jzpos, 1, _fs->GetCurrentPos() - jzpos); _fs->RestoreOpt(); END_BREAKABLE_BLOCK(continuetrg); END_SCOPE(); _complexity_level--; } // Returns true if any Id node behind a function/lambda boundary inside body // matches name0 or name1 (either may be null). Over-approximates: an inner // same-named local that shadows the outer still counts as a hit. Cost of a // false positive is one MOVE per binding + one OP_CLOSE per iteration, so // the simpler scan wins. class CaptureScanVisitor : public Visitor { public: bool captured = false; int fnDepth = 0; const char *name0 = nullptr; const char *name1 = nullptr; uint64_t bitFilter = 0; // bit i set if any tracked name starts with char c where c%64 == i virtual void visitId(Id *id) override { if (captured || fnDepth == 0) return; const char *nm = id->name(); if ((bitFilter & (uint64_t(1) << (uint8_t(nm[0]) & 63))) == 0) return; if ((name0 && strcmp(name0, nm) == 0) || (name1 && strcmp(name1, nm) == 0)) captured = true; } virtual void visitFunctionExpr(FunctionExpr *f) override { if (captured) return; fnDepth++; f->visitChildren(this); fnDepth--; } }; static bool scanCaptureForNames(Statement *body, const char *name0, const char *name1) { if (!name0 && !name1) return false; CaptureScanVisitor v; v.name0 = name0; v.name1 = name1; if (name0) v.bitFilter |= uint64_t(1) << (uint8_t(name0[0]) & 63); if (name1) v.bitFilter |= uint64_t(1) << (uint8_t(name1[0]) & 63); body->visit(&v); return v.captured; } void CodeGenVisitor::visitForeachStatement(ForeachStatement *foreachLoop) { addLineNumber(foreachLoop); _complexity_level++; BEGIN_SCOPE(); visitForValue(foreachLoop->container()); SQInteger container = _fs->TopTarget(); assert(foreachLoop->val()->op() == TO_VAR && ((VarDecl *)foreachLoop->val())->initializer() == nullptr); const char *userValName = ((VarDecl *)foreachLoop->val())->name(); const char *userIdxName = foreachLoop->idx() ? ((VarDecl *)foreachLoop->idx())->name() : nullptr; // Parser-emitted destructuring surrogate: source code can't reference it // (lexer can't produce '@'), so it never gets captured and never needs // per-iter rebinding. bool valIsSurrogate = userValName && strncmp(userValName, "@FE_VAL", 7) == 0; // Detect if any user-visible name (idx, val) is captured by an inner // function inside body. When captured, we re-bind those names in a // per-iteration scope inside the body region so END_SCOPE emits _OP_CLOSE // every iter, giving each closure its own frozen copy of the value. bool perIterScope = scanCaptureForNames(foreachLoop->body(), userIdxName, (userValName && !valIsSurrogate) ? userValName : nullptr); // Outer-scope names: when a user-visible name will be re-bound per-iter, // hide the outer slot under a surrogate so source references resolve to // the per-iter binding. SQObjectPtr idxName = (userIdxName && !perIterScope) ? _fs->CreateString(userIdxName) : _fs->CreateString("@INDEX@"); SQInteger indexpos = _fs->PushLocalVariable(idxName, SQCompiletimeVarInfo{}); SQObjectPtr valName = (userValName && !valIsSurrogate && perIterScope) ? _fs->CreateString("@valouter@") : _fs->CreateString(userValName); SQInteger valpos = _fs->PushLocalVariable(valName, SQCompiletimeVarInfo{}); //push reference index _fs->PushLocalVariable(_fs->CreateString("@ITERATOR@"), SQCompiletimeVarInfo{}); //use invalid id to make it inaccessible _fs->AddInstruction(_OP_PREFOREACH, container, 0, indexpos); SQInteger preForEachPos = _fs->GetCurrentPos(); _fs->AddInstruction(_OP_POSTFOREACH, container, 0, indexpos); SQInteger postForEachPos = _fs->GetCurrentPos(); BEGIN_BREAKABLE_BLOCK(); if (perIterScope) { BEGIN_SCOPE(); if (userIdxName) { SQInteger pos = _fs->PushLocalVariable(_fs->CreateString(userIdxName), SQCompiletimeVarInfo{}); _fs->AddInstruction(_OP_MOVE, pos, indexpos); } if (userValName && !valIsSurrogate) { SQInteger pos = _fs->PushLocalVariable(_fs->CreateString(userValName), SQCompiletimeVarInfo{}); _fs->AddInstruction(_OP_MOVE, pos, valpos); } foreachLoop->body()->visit(this); END_SCOPE(); } else { foreachLoop->body()->visit(this); } SQInteger continuePos = _fs->GetCurrentPos(); SQInteger forEachLabelPos = _fs->GetCurrentPos() + 1; _fs->AddInstruction(_OP_FOREACH, container, postForEachPos - forEachLabelPos, indexpos); //ip is already + 1 now _fs->SetInstructionParam(preForEachPos, 1, forEachLabelPos - preForEachPos); //ip is already + 1 now _fs->SetInstructionParam(postForEachPos, 1, _fs->GetCurrentPos() - postForEachPos); //ip is already + 1 now END_BREAKABLE_BLOCK(continuePos); //restore the local variable stack(remove index,val and ref idx) _fs->PopTarget(); END_SCOPE(); _complexity_level--; } void CodeGenVisitor::visitSwitchStatement(SwitchStatement *swtch) { addLineNumber(swtch); BEGIN_SCOPE(); visitForValue(swtch->expression()); SQInteger expr = _fs->TopTarget(); SQInteger tonextcondjmp = -1; SQInteger skipcondjmp = -1; SQInteger __nbreaks__ = _fs->_unresolvedbreaks.size(); _fs->_breaktargets.push_back(0); _fs->_blockstacksizes.push_back(_scope.stacksize); ArenaVector &cases = swtch->cases(); for (SQUnsignedInteger i = 0; i < cases.size(); ++i) { if (i) { _fs->AddInstruction(_OP_JMP, 0, 0); skipcondjmp = _fs->GetCurrentPos(); _fs->SetInstructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp); } const SwitchCase &c = cases[i]; visitForValueMaybeStaticMemo(c.val); SQInteger trg = _fs->PopTarget(); SQInteger eqtarget = trg; bool local = _fs->IsLocal(trg); if (local) { eqtarget = _fs->PushTarget(); //we need to allocate a extra reg } _fs->AddInstruction(_OP_EQ, eqtarget, trg, expr); _fs->AddInstruction(_OP_JZ, eqtarget, 0); if (local) { _fs->PopTarget(); } //end condition if (skipcondjmp != -1) { _fs->SetInstructionParam(skipcondjmp, 1, (_fs->GetCurrentPos() - skipcondjmp)); } tonextcondjmp = _fs->GetCurrentPos(); BEGIN_SCOPE(); c.stmt->visit(this); END_SCOPE(); } if (tonextcondjmp != -1) _fs->SetInstructionParam(tonextcondjmp, 1, _fs->GetCurrentPos() - tonextcondjmp); const SwitchCase &d = swtch->defaultCase(); if (d.stmt) { BEGIN_SCOPE(); d.stmt->visit(this); END_SCOPE(); } _fs->PopTarget(); __nbreaks__ = _fs->_unresolvedbreaks.size() - __nbreaks__; if (__nbreaks__ > 0) ResolveBreaks(_fs, __nbreaks__); _fs->_breaktargets.pop_back(); _fs->_blockstacksizes.pop_back(); END_SCOPE(); } void CodeGenVisitor::visitTryStatement(TryStatement *tryStmt) { addLineNumber(tryStmt); _fs->AddInstruction(_OP_PUSHTRAP, 0, 0); _fs->_traps++; if (_fs->_breaktargets.size()) _fs->_breaktargets.top()++; if (_fs->_continuetargets.size()) _fs->_continuetargets.top()++; SQInteger trappos = _fs->GetCurrentPos(); { BEGIN_SCOPE(); tryStmt->tryStatement()->visit(this); END_SCOPE(); } _fs->_traps--; _fs->AddInstruction(_OP_POPTRAP, 1, 0); if (_fs->_breaktargets.size()) _fs->_breaktargets.top()--; if (_fs->_continuetargets.size()) _fs->_continuetargets.top()--; _fs->AddInstruction(_OP_JMP, 0, 0); SQInteger jmppos = _fs->GetCurrentPos(); _fs->SetInstructionParam(trappos, 1, (_fs->GetCurrentPos() - trappos)); { BEGIN_SCOPE(); SQInteger ex_target = _fs->PushLocalVariable(_fs->CreateString(tryStmt->exceptionId()->name()), SQCompiletimeVarInfo{}); _fs->SetInstructionParam(trappos, 0, ex_target); tryStmt->catchStatement()->visit(this); _fs->SetInstructionParams(jmppos, 0, (_fs->GetCurrentPos() - jmppos), 0); END_SCOPE(); } } void CodeGenVisitor::visitBreakStatement(BreakStatement *breakStmt) { addLineNumber(breakStmt); if (_fs->_breaktargets.size() <= 0) reportDiagnostic(breakStmt, DiagnosticsId::DI_LOOP_CONTROLLER_NOT_IN_LOOP, "break"); if (_fs->_breaktargets.top() > 0) { _fs->AddInstruction(_OP_POPTRAP, _fs->_breaktargets.top(), 0); } RESOLVE_OUTERS(); _fs->AddInstruction(_OP_JMP, 0, -1234); _fs->_unresolvedbreaks.push_back(_fs->GetCurrentPos()); } void CodeGenVisitor::visitContinueStatement(ContinueStatement *continueStmt) { addLineNumber(continueStmt); if (_fs->_continuetargets.size() <= 0 || _fs->_continuetargets.top() < 0) reportDiagnostic(continueStmt, DiagnosticsId::DI_LOOP_CONTROLLER_NOT_IN_LOOP, "continue"); if (_fs->_continuetargets.top() > 0) { _fs->AddInstruction(_OP_POPTRAP, _fs->_continuetargets.top(), 0); } RESOLVE_OUTERS(); _fs->AddInstruction(_OP_JMP, 0, -1234); _fs->_unresolvedcontinues.push_back(_fs->GetCurrentPos()); } void CodeGenVisitor::visitTerminateStatement(TerminateStatement *terminator) { addLineNumber(terminator); if (terminator->argument()) { visitForValueMaybeStaticMemo(terminator->argument()); } } void CodeGenVisitor::visitReturnStatement(ReturnStatement *retStmt) { SQInteger retexp = _fs->GetCurrentPos() + 1; visitTerminateStatement(retStmt); if (!_fs->_expr_block_results.empty()) { // Inside a CodeBlockExpr: return is compiled as a break (MOVE+JMP), // not a real function return. Only pop traps local to this block, // not the entire function's traps. if (_fs->_breaktargets.top() > 0) { _fs->AddInstruction(_OP_POPTRAP, _fs->_breaktargets.top(), 0); } if (retStmt->argument()) { _fs->AddInstruction(_OP_MOVE, _fs->_expr_block_results.back(), _fs->PopTarget()); } else { _fs->AddInstruction(_OP_LOADNULLS, _fs->_expr_block_results.back(), 1); } RESOLVE_OUTERS(); _fs->AddInstruction(_OP_JMP, 0, -1234); _fs->_unresolvedbreaks.push_back(_fs->GetCurrentPos()); } else { if (_fs->_traps > 0) { _fs->AddInstruction(_OP_POPTRAP, _fs->_traps, 0); } if (retStmt->argument()) { _fs->_returnexp = retexp; int target = _fs->PopTarget(); if (!_fs->_bgenerator) { if (!checkInferredType(retStmt, retStmt->argument(), _fs->_result_type_mask)) EmitCheckType(target, _fs->_result_type_mask); } _fs->AddInstruction(_OP_RETURN, 1, target); } else { if ((_fs->_result_type_mask & _RT_NULL) == 0 && !_fs->_bgenerator) reportDiagnostic(retStmt, DiagnosticsId::DI_TYPE_DIFFERS, "Return"); _fs->_returnexp = -1; _fs->AddInstruction(_OP_RETURN, 0xFF, 0); } } } void CodeGenVisitor::visitYieldStatement(YieldStatement *yieldStmt) { SQInteger retexp = _fs->GetCurrentPos() + 1; _fs->_bgenerator = true; visitTerminateStatement(yieldStmt); if (yieldStmt->argument()) { _fs->_returnexp = retexp; int target = _fs->PopTarget(); if (!checkInferredType(yieldStmt, yieldStmt->argument(), _fs->_result_type_mask)) EmitCheckType(target, _fs->_result_type_mask); _fs->AddInstruction(_OP_YIELD, 1, target, _fs->GetStackSize()); } else { if ((_fs->_result_type_mask & _RT_NULL) == 0) reportDiagnostic(yieldStmt, DiagnosticsId::DI_TYPE_DIFFERS, "Yield"); _fs->_returnexp = -1; _fs->AddInstruction(_OP_YIELD, 0xFF, 0, _fs->GetStackSize()); } } void CodeGenVisitor::visitThrowStatement(ThrowStatement *throwStmt) { visitTerminateStatement(throwStmt); _fs->AddInstruction(_OP_THROW, _fs->PopTarget()); } bool CodeGenVisitor::DoesObjectContainOnlySimpleObjects(const SQObject &obj, int depth) { if (sq_isnumeric(obj) || sq_isstring(obj) || sq_isbool(obj) || sq_isnull(obj)) return true; if (sq_is_pure_function(const_cast(&obj))) return true; if (depth > 80) return false; if (sq_istable(obj)) { SQInteger idx = 0; SQObjectPtr key, val; while ((idx = _table(obj)->Next(true, SQObjectPtr(idx), key, val)) != -1) { if (!DoesObjectContainOnlySimpleObjects(key, depth + 1)) return false; if (!DoesObjectContainOnlySimpleObjects(val, depth + 1)) return false; } return true; } if (sq_isclass(obj)) { SQInteger idx = 0; SQObjectPtr key, val; while ((idx = _class(obj)->Next(SQObjectPtr(idx), key, val)) != -1) { if (!DoesObjectContainOnlySimpleObjects(key, depth + 1)) return false; if (!DoesObjectContainOnlySimpleObjects(val, depth + 1)) return false; } return true; } if (sq_isarray(obj)) { SQInteger idx = 0; SQObjectPtr key, val; while ((idx = _array(obj)->Next(SQObjectPtr(idx), key, val)) != -1) { if (!DoesObjectContainOnlySimpleObjects(val, depth + 1)) return false; } return true; } return false; } bool CodeGenVisitor::IsSimpleConstant(const SQObject &name) { SQObjectPtr c; if (!IsConstant(name, c)) return false; return DoesObjectContainOnlySimpleObjects(c, 0); } Expr *CodeGenVisitor::skipConstFreezePure(Expr *expr) { for (;;) { if (expr->op() == TO_STATIC_MEMO || expr->op() == TO_PAREN) expr = static_cast(expr)->argument(); else if (isFreezeCall(expr)) expr = static_cast(expr)->arguments()[0]; else break; } return expr; } class GuaranteedPurityCheckVisitor : public Visitor { SQVM *_vm; SQFuncState *_fs = nullptr; public: bool pure; GuaranteedPurityCheckVisitor(SQVM *vm, SQFuncState *fs) : pure(true), _vm(vm), _fs(fs) {} virtual void visitNode(Node *node) override { if (!pure) return; switch (node->op()) { case TO_THROW: case TO_YIELD: pure = false; break; case TO_ID: { SQObjectPtr name(SQString::Create(_ss(_vm), static_cast(node)->name())); SQCompiletimeVarInfo varInfo; if (_fs->GetLocalVariable(name, varInfo) != -1 || _fs->GetOuterVariable(name, varInfo) != -1) { pure = false; break; } } break; default: node->visitChildren(this); break; } } }; bool CodeGenVisitor::IsGuaranteedPureFunction(Node *expr) { GuaranteedPurityCheckVisitor visitor(_vm, _fs); expr->visit(&visitor); return visitor.pure; } bool CodeGenVisitor::isConstScoredMethodCall(GetFieldExpr *getField) { Expr *receiver = deparen(getField->receiver()); if (getSubtreeConstScoreImpl(receiver) == 0) { _variable_node = getField->receiver(); return false; } SQInteger type = inferReceiverType(receiver); SQObjectPtr method = GetTypeMethod(type, getField->fieldName()); return sq_is_pure_function(&method); } bool CodeGenVisitor::isConstScoredDirectCall(CallExpr *callExpr) { if (isPureFunctionCall(callExpr)) return true; Expr *callee = deparen(callExpr->callee()); SQObjectPtr calleeName = _fs->CreateString(callee->asId()->name()); SQCompiletimeVarInfo varInfo; if (findConstScoredVar(calleeName, varInfo, VF_INIT_WITH_PURE)) return true; SQObjectPtr c; return IsConstant(calleeName, c) && sq_is_pure_function(&c); } SQInteger CodeGenVisitor::inferReceiverType(Expr *receiver) { if (receiver->op() == TO_ID) { SQObjectPtr c; SQObjectPtr name = _fs->CreateString(receiver->asId()->name()); if (IsConstant(name, c)) { SQInteger type = sq_type(c); if ((type == OT_TABLE || type == OT_CLASS || type == OT_ARRAY) && (c._flags & SQOBJ_FLAG_IMMUTABLE) == 0) return -1; return type; } SQCompiletimeVarInfo varInfo; if (findConstScoredVar(name, varInfo, VF_INIT_WITH_CONST | VF_INIT_WITH_FREEZE | VF_INIT_WITH_PURE)) receiver = skipConstFreezePure(varInfo.initializer); } if (receiver->op() == TO_LITERAL) { switch (receiver->asLiteral()->kind()) { case LK_STRING: return OT_STRING; case LK_INT: return OT_INTEGER; case LK_FLOAT: return OT_FLOAT; case LK_BOOL: return OT_BOOL; case LK_NULL: return OT_NULL; default: return -1; } } if (receiver->op() == TO_TABLE) return OT_TABLE; if (receiver->op() == TO_CLASS) return OT_CLASS; if (receiver->op() == TO_ARRAY) return OT_ARRAY; return -1; } bool CodeGenVisitor::findConstScoredVar(const SQObjectPtr &name, SQCompiletimeVarInfo &varInfo, unsigned requiredFlags) { if (_fs->GetLocalVariable(name, varInfo) == -1 && _fs->GetOuterVariable(name, varInfo) == -1) return false; return (varInfo.var_flags & requiredFlags) != 0 && (varInfo.var_flags & (VF_DESTRUCTURED | VF_ASSIGNABLE)) == 0; } // 0 - not const int CodeGenVisitor::getSubtreeConstScore(Node *node, bool visit_arrays_and_tables) { _variable_node = nullptr; _visit_arrays_and_tables = visit_arrays_and_tables; return getSubtreeConstScoreImpl(node); } int CodeGenVisitor::getSubtreeConstScoreImpl(Node *node) { switch (node->op()) { case TO_NULLC: case TO_OROR: case TO_ANDAND: case TO_OR: case TO_XOR: case TO_AND: case TO_NE: case TO_EQ: case TO_3CMP: case TO_GE: case TO_GT: case TO_LE: case TO_LT: case TO_IN: case TO_INSTANCEOF: case TO_USHR: case TO_SHR: case TO_SHL: case TO_MUL: case TO_DIV: case TO_MOD: case TO_ADD: case TO_SUB: { BinExpr *binExpr = static_cast(node); int s1 = getSubtreeConstScoreImpl(binExpr->lhs()); int s2 = getSubtreeConstScoreImpl(binExpr->rhs()); if (s1 == 0 || s2 == 0) return 0; return s1 + s2 + 1; } case TO_NOT: case TO_BNOT: case TO_NEG: case TO_TYPEOF: case TO_PAREN: { UnExpr *unExpr = static_cast(node); int s = getSubtreeConstScoreImpl(unExpr->argument()); return s > 0 ? s + 1 : 0; } case TO_STATIC_MEMO: { return 9999; } case TO_INLINE_CONST: { return 1; // the same as const identifier } case TO_TERNARY: { TerExpr *terExpr = static_cast(node); int s1 = getSubtreeConstScoreImpl(terExpr->a()); int s2 = getSubtreeConstScoreImpl(terExpr->b()); int s3 = getSubtreeConstScoreImpl(terExpr->c()); if (s1 > 0 && s2 > 0 && s3 > 0) return s1 + s2 + s3 + 1; return 0; } case TO_CALL: { CallExpr *callExpr = static_cast(node); Expr *callee = deparen(callExpr->callee()); if (callee->op() == TO_GETFIELD) { if (!isConstScoredMethodCall(callee->asGetField())) { if (!_variable_node) _variable_node = node; return 0; } } else if (callee->op() == TO_ID) { if (!isConstScoredDirectCall(callExpr)) { _variable_node = node; return 0; } } else { _variable_node = node; return 0; } int res = 20; for (Expr *arg : callExpr->arguments()) { int s = getSubtreeConstScoreImpl(arg); if (s == 0) return 0; res += s + 1; } return res; } case TO_ID: { Id *id = node->asId(); SQObjectPtr name = _fs->CreateString(id->name()); SQObjectPtr c; if (IsConstant(name, c)) return 1; SQCompiletimeVarInfo varInfo; if (findConstScoredVar(name, varInfo, VF_INIT_WITH_CONST | VF_INIT_WITH_FREEZE)) return 1; _variable_node = node; return 0; } case TO_GETSLOT: { GetSlotExpr *expr = static_cast(node); Expr *receiver = expr->receiver(); int s1 = getSubtreeConstScoreImpl(receiver); if (s1 == 0) { _variable_node = receiver; return 0; } if (receiver->op() == TO_ID) { SQObjectPtr name = _fs->CreateString(receiver->asId()->name()); SQCompiletimeVarInfo varInfo; if (findConstScoredVar(name, varInfo, VF_INIT_WITH_CONST | VF_INIT_WITH_FREEZE | VF_INIT_WITH_PURE)) receiver = skipConstFreezePure(varInfo.initializer); } if (receiver->op() == TO_LITERAL) { if (receiver->asLiteral()->kind() != LK_STRING) { _variable_node = expr->receiver(); return 0; } } else if (receiver->op() == TO_TABLE || receiver->op() == TO_CLASS) { // Table and class expressions are allowed } else if (receiver->op() == TO_ID) { SQObjectPtr name = _fs->CreateString(receiver->asId()->name()); if (!IsSimpleConstant(name)) { _variable_node = expr->receiver(); return 0; } } else if (receiver->op() != TO_ARRAY) { _variable_node = expr->receiver(); return 0; } int s2 = getSubtreeConstScoreImpl(expr->key()); if (s2 == 0) { _variable_node = expr->key(); return 0; } return s1 + s2 + 1; } case TO_GETFIELD: { GetFieldExpr *expr = static_cast(node); int s = getSubtreeConstScoreImpl(expr->receiver()); if (s == 0) { _variable_node = expr->receiver(); return 0; } Expr *receiver = deparen(expr->receiver()); if (receiver->op() == TO_ID) { SQObjectPtr name = _fs->CreateString(receiver->asId()->name()); SQObjectPtr c; if (IsConstant(name, c) || IsSimpleConstant(name)) return s + 2; } else if (receiver->op() == TO_GETFIELD) return s + 2; _variable_node = expr->receiver(); return 0; } case TO_LITERAL: return 1; case TO_FUNCTION: { if (IsGuaranteedPureFunction(node)) return 10; else { _variable_node = node; return 0; } } case TO_TABLE: case TO_CLASS: { if (!_visit_arrays_and_tables) { _variable_node = node; return 0; } ArenaVector &members = static_cast(node)->members(); int res = 50; for (TableMember &m : members) { int s1 = getSubtreeConstScoreImpl(m.key); int s2 = getSubtreeConstScoreImpl(m.value); if (s1 == 0 || s2 == 0) return 0; res += s1 + s2 + 1; } return res; } case TO_ARRAY: { if (!_visit_arrays_and_tables) { _variable_node = node; return 0; } ArenaVector &initializers = static_cast(node)->initializers(); int res = 50; for (Expr *expr : initializers) { int s = getSubtreeConstScoreImpl(expr); if (s == 0) return 0; res += s + 1; } return res; } default: _variable_node = node; return 0; } } void CodeGenVisitor::visitCodeBlockExpr(CodeBlockExpr *expr) { maybeAddInExprLine(expr); SQInteger resultTarget = _fs->PushTarget(); BEGIN_SCOPE(); SQInteger nbreaks = _fs->_unresolvedbreaks.size(); _fs->_breaktargets.push_back(0); _fs->_continuetargets.push_back(-1); // sentinel: not a loop, 'continue' is invalid _fs->_blockstacksizes.push_back(_scope.stacksize); _fs->_expr_block_results.push_back(resultTarget); expr->block()->visit(this); //_fs->AddInstruction(_OP_LOADNULLS, resultTarget, 1); nbreaks = _fs->_unresolvedbreaks.size() - nbreaks; if (nbreaks > 0) ResolveBreaks(_fs, nbreaks); _fs->_expr_block_results.pop_back(); _fs->_breaktargets.pop_back(); _fs->_continuetargets.pop_back(); _fs->_blockstacksizes.pop_back(); END_SCOPE(); } void CodeGenVisitor::visitExprStatement(ExprStatement *stmt) { addLineNumber(stmt); visitForValue(stmt->expression()); _fs->DiscardTarget(); } void CodeGenVisitor::generateTableExpr(TableExpr *tableDecl) { bool isKlass = tableDecl->op() == TO_CLASS; const auto &members = tableDecl->members(); ArenaVector memberConstantKeys(_arena); for (SQUnsignedInteger i = 0; i < members.size(); ++i) { const TableMember &m = members[i]; #if SQ_LINE_INFO_IN_STRUCTURES if (i < 100 && m.key->lineStart() != -1) { _fs->AddLineInfos(m.key->lineStart(), false, false); } #endif CheckMemberUniqueness(memberConstantKeys, m.key); bool isConstKey = m.key->op() == TO_LITERAL && !isKlass; SQInteger constantI; if (isConstKey) { switch(m.key->asLiteral()->kind()) { case LK_STRING: constantI = _fs->GetConstant(_fs->CreateString(m.key->asLiteral()->s())); break; case LK_INT: constantI = _fs->GetNumericConstant(m.key->asLiteral()->i()); break; case LK_FLOAT: constantI = _fs->GetNumericConstant(m.key->asLiteral()->f()); break; case LK_BOOL: constantI = _fs->GetConstant(SQObjectPtr(m.key->asLiteral()->b())); break; case LK_NULL: constantI = _fs->GetConstant(SQObjectPtr()); break; default: assert(0); break; } //isConstKey = constantI < 256; // since currently we store in arg1, it is sufficient } if (!isConstKey) visitForValueMaybeStaticMemo(m.key); visitForValueMaybeStaticMemo(m.value); SQInteger val = _fs->PopTarget(); SQInteger key = isConstKey ? constantI : _fs->PopTarget(); SQInteger table = _fs->TopTarget(); //<AddInstruction(_OP_NEWSLOTA, m.isStatic() ? NEW_SLOT_STATIC_FLAG : 0, table, key, val); } else { _fs->AddInstruction(isConstKey ? _OP_NEWSLOTK : _OP_NEWSLOT, 0xFF, key, table, val); } } } void CodeGenVisitor::addPatchDocObjectInstruction(const DocObject &docObject) { int idx = (_ss(_vm)->doc_object_index += 2); SaveDocstringToVM((void *)size_t(idx), docObject); _fs->AddInstruction(_OP_PATCH_DOCOBJ, _fs->TopTarget(), idx, 0, 0); } void CodeGenVisitor::visitTableExpr(TableExpr *tableExpr) { addLineNumber(tableExpr); _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(), tableExpr->members().size(), 0, NEWOBJ_TABLE); if (!tableExpr->docObject.isEmpty()) addPatchDocObjectInstruction(tableExpr->docObject); generateTableExpr(tableExpr); } void CodeGenVisitor::visitClassExpr(ClassExpr *klass) { addLineNumber(klass); Expr *baseExpr = klass->classBase(); SQInteger baseIdx = -1; if (baseExpr) { visitForValue(baseExpr); baseIdx = _fs->PopTarget(); } _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(), baseIdx, 0, NEWOBJ_CLASS); if (!klass->docObject.isEmpty()) addPatchDocObjectInstruction(klass->docObject); generateTableExpr(klass); } static const char *varDeclKindDescription(VarDecl *var) { Expr *init = var->initializer(); if (init == NULL) { return var->isAssignable() ? "Local variable" : "Named binding"; } else { switch (init->op()) { case TO_FUNCTION: return "Function"; case TO_CLASS: return "Class"; case TO_TABLE: return "Named binding"; // <== is that correct? default: return var->isAssignable() ? "Local variable" : "Named binding"; } } } bool CodeGenVisitor::isFreezeCall(Expr *node) { node = deparen(node); if (node->op() == TO_CALL) { CallExpr *callExpr = static_cast(node); Expr *callee = deparen(callExpr->callee()); if (callee->op() == TO_ID && !strcmp(callee->asId()->name(), "freeze") && callExpr->arguments().size() == 1) return true; } return false; } bool CodeGenVisitor::isPureFunctionCall(Expr *node) { node = deparen(node); if (node->op() == TO_CALL) { CallExpr *callExpr = static_cast(node); Expr *callee = deparen(callExpr->callee()); if (callee->op() == TO_ID) { SQObjectPtr name(SQString::Create(_ss(_vm), callee->asId()->name())); SQObjectPtr constant; // Find the function in the local scope SQInteger pos = -1; SQCompiletimeVarInfo varInfo; if ((pos = _fs->GetLocalVariable(name, varInfo)) != -1 || (pos = _fs->GetOuterVariable(name, varInfo)) != -1) { if (varInfo.initializer && varInfo.initializer->op() == TO_FUNCTION) { FunctionExpr *funcExpr = varInfo.initializer->asFunctionExpr(); if (funcExpr->isPure()) { return true; } } } else if (IsConstant(name, constant)) { if (sq_is_pure_function(&constant)) { return true; } } } } return false; } void CodeGenVisitor::visitVarDecl(VarDecl *var) { addLineNumber(var); const char *name = var->name(); char varFlags = var->isAssignable() ? VF_ASSIGNABLE : 0; if (var->isDestructured()) varFlags |= VF_DESTRUCTURED; if (_complexity_level == 0) varFlags |= VF_FIRST_LEVEL; SQObjectPtr varName = _fs->CreateString(name); CheckDuplicateLocalIdentifier(var, varName, varDeclKindDescription(var), false); if (var->initializer()) { bool isStaticMemoInitializer = visitForValueMaybeStaticMemo(var->initializer()); if (isStaticMemoInitializer) varFlags |= VF_INIT_WITH_CONST; else if (isFreezeCall(var->initializer())) varFlags |= VF_INIT_WITH_FREEZE; else if (isPureFunctionCall(var->initializer())) varFlags |= VF_INIT_WITH_PURE; SQInteger src = _fs->PopTarget(); SQInteger dest = _fs->PushTarget(); if (dest != src) { _fs->AddInstruction(_OP_MOVE, dest, src); } if (!checkInferredType(var, var->initializer(), var->getTypeMask())) EmitCheckType(dest, var->getTypeMask()); } else { _fs->AddInstruction(_OP_LOADNULLS, _fs->PushTarget(), 1); if ((var->getTypeMask() & _RT_NULL) == 0 && !var->isDestructured()) reportDiagnostic(var, DiagnosticsId::DI_TYPE_DIFFERS, "Assigned null"); } _last_declared_var_pos = _fs->PopTarget(); _fs->PushLocalVariable(varName, SQCompiletimeVarInfo{varFlags, var->getTypeMask(), var->initializer()}); } void CodeGenVisitor::visitDeclGroup(DeclGroup *group) { addLineNumber(group); const auto &declarations = group->declarations(); for (Decl *d : declarations) { d->visit(this); } } void CodeGenVisitor::visitDestructuringDecl(DestructuringDecl *destruct) { addLineNumber(destruct); ArenaVector targets(_arena); const auto &declarations = destruct->declarations(); for (auto d : declarations) { d->visit(this); assert(_last_declared_var_pos != -1); targets.push_back(_last_declared_var_pos); _last_declared_var_pos = -1; } visitForValueMaybeStaticMemo(destruct->initExpression()); SQInteger src = _fs->TopTarget(); _fs->PushTarget(); // key for (SQUnsignedInteger i = 0; i < declarations.size(); ++i) { VarDecl *d = declarations[i]; SQInteger flags = d->initializer() ? OP_GET_FLAG_NO_ERROR | OP_GET_FLAG_KEEP_VAL : 0; _fs->AddInstruction(_OP_GETK, targets[i], destruct->type() == DT_ARRAY ? _fs->GetNumericConstant((SQInteger)i) : _fs->GetConstant(_fs->CreateString(d->name())), src, flags); EmitCheckType(targets[i], d->getTypeMask()); } _fs->PopTarget(); _fs->PopTarget(); } void CodeGenVisitor::visitParamDecl(ParamDecl *param) { if (param->hasDefaultValue()) { checkInferredType(param, param->defaultValue(), param->getTypeMask()); visitForValueMaybeStaticMemo(param->defaultValue()); } } SQObjectPtr CodeGenVisitor::compileFunc(FunctionExpr *funcDecl, bool is_const, sqvector *out_defparam_values) { _complexity_level++; // For hoisted closures, skip overriding the parent's line number — // the enclosing VarDecl already set the correct line for the _OP_CLOSURE // instruction. Using the original lambda line would corrupt stack traces. if (funcDecl->hoistingLevel() == 0) addLineNumber(funcDecl); SQFuncState *savedChildFsAtRoot = _childFs; // may be non-null when processing default argument that is declared as function/lambda _childFs = _fs->PushChildState(_ss(_vm)); _childFs->_name = _fs->CreateString(funcDecl->name()); _childFs->_sourcename = _fs->_sourcename = SQString::Create(_ss(_vm), funcDecl->sourceName()); _childFs->_varparams = funcDecl->isVararg(); _childFs->_purefunction = funcDecl->isPure(); _childFs->_nodiscard = funcDecl->isNodiscard(); _childFs->_result_type_mask = funcDecl->getResultTypeMask(); SQInteger defparams = 0; _childFs->AddParameter(_fs->CreateString("this"), _RT_INSTANCE | _RT_TABLE | _RT_CLASS | _RT_USERDATA | _RT_NULL); assert(is_const == (out_defparam_values!=nullptr)); for (auto param : funcDecl->parameters()) { _childFs->AddParameter(_fs->CreateString(param->name()), param->getTypeMask()); if (is_const) { if (param->hasDefaultValue()) { ConstGenVisitor constVisitor(_vm, _fs, _ctx, *this); SQObjectPtr defValue; constVisitor.process(param->defaultValue(), defValue); out_defparam_values->push_back(defValue); const SQInteger dummy = -1; // The actual values is not used. // Adding to internal vector only to provide the correct number of default arguments // for SQFuncState::BuildProto() call. // SQClosure's default argument values will be initialized in compileConstFunc() _childFs->AddDefaultParam(dummy); ++defparams; } } else { // The logic is split over visitParamDecl() and here // TODO: move to the single place param->visit(this); if (param->hasDefaultValue()) { _childFs->AddDefaultParam(_fs->TopTarget()); ++defparams; } } } if (!is_const) { for (SQInteger n = 0; n < defparams; n++) { _fs->PopTarget(); } } Block *body = funcDecl->body(); { assert(_childFs->_parent == _fs); SQFuncState *savedChildFsAtBody = _childFs; _fs = _childFs; _childFs = NULL; for (auto param : funcDecl->parameters()) { if (param->getDestructuring()) param->getDestructuring()->visit(this); } SQInteger startLine = body->lineStart(); if (startLine != -1) savedChildFsAtBody->AddLineInfos(startLine, true, false); funcDecl->body()->visit(this); _childFs = savedChildFsAtBody; _fs = _childFs->_parent; } _childFs->AddLineInfos(body->lineEnd(), true, true); _childFs->AddInstruction(_OP_RETURN, -1); if (GLOBAL_OPTIMIZATION_SWITCH && !(_childFs->lang_features & LF_DISABLE_OPTIMIZER)) { SQOptimizer opt(*_childFs); opt.optimize(); _childFs->CheckForPurity(); } _childFs->SetStackSize(0); _childFs->_hoistLevel = funcDecl->hoistingLevel(); SQFunctionProto *funcProto = _childFs->BuildProto(); SQObjectPtr result(funcProto); _fs->PopChildState(); _childFs = savedChildFsAtRoot; SaveDocstringToVM((void *)funcProto, funcDecl->docObject); _complexity_level--; return result; } void CodeGenVisitor::visitFunctionExpr(FunctionExpr *funcDecl) { bool old_inside_static_memo = _inside_static_memo; _inside_static_memo = false; SQObjectPtr objFuncProto = compileFunc(funcDecl, false, nullptr); _fs->_functions.push_back(objFuncProto); _fs->AddInstruction(_OP_CLOSURE, _fs->PushTarget(), _fs->_functions.size() - 1, funcDecl->isLambda() ? 1 : 0); _inside_static_memo = old_inside_static_memo; } SQObjectPtr CodeGenVisitor::compileConstFunc(FunctionExpr *funcDecl) { sqvector defParamValues(nullptr); SQObjectPtr objFuncProto = compileFunc(funcDecl, true, &defParamValues); if (_funcproto(objFuncProto)->_noutervalues) reportDiagnostic(funcDecl, DiagnosticsId::DI_GENERAL_COMPILE_ERROR, "const function cannot have outer variables"); SQClosure *closure = SQClosure::Create(_ss(_vm), _funcproto(objFuncProto)); SQInteger ndefparams = _funcproto(objFuncProto)->_ndefaultparams; assert((SQInteger)defParamValues.size() == ndefparams); for (SQInteger i = 0; i < ndefparams; i++) { closure->_defaultparams[i] = defParamValues[i]; } return SQObjectPtr(closure); } void CodeGenVisitor::SaveDocstringToVM(void *key, const DocObject &docObject) { if (docObject.getDocString()) { SQObjectPtr docValue(SQString::Create(_ss(_vm), docObject.getDocString())); SQObjectPtr docKey; docKey._type = OT_USERPOINTER; docKey._unVal.pUserPointer = key; _table(_ss(_vm)->doc_objects)->NewSlot(docKey, docValue); } } SQTable* CodeGenVisitor::GetScopedConstsTable() { assert(!_scopedconsts.empty()); SQObjectPtr &consts = _scopedconsts.top(); if (sq_type(consts) != OT_TABLE) consts = SQTable::Create(_ss(_vm), 0); return _table(consts); } SQObjectPtr CodeGenVisitor::convertLiteral(LiteralExpr *lit) { SQObjectPtr ret{}; switch (lit->kind()) { case LK_STRING: return SQObjectPtr(SQString::Create(_fs->_sharedstate, lit->s(), -1)); case LK_FLOAT: ret._type = OT_FLOAT; ret._unVal.fFloat = lit->f(); break; case LK_INT: ret._type = OT_INTEGER; ret._unVal.nInteger = lit->i(); break; case LK_BOOL: ret._type = OT_BOOL; ret._unVal.nInteger = lit->b() ? 1 : 0; break; case LK_NULL: ret._type = OT_NULL; ret._unVal.raw = 0; break; } return ret; } void CodeGenVisitor::visitConstDecl(ConstDecl *decl) { addLineNumber(decl); SQObjectPtr id = _fs->CreateString(decl->name()); CheckDuplicateLocalIdentifier(decl, id, "Constant", decl->isGlobal() && !(_fs->lang_features & LF_FORBID_GLOBAL_CONST_REWRITE)); SQTable *constantsTbl = decl->isGlobal() ? _table(_ss(_vm)->_consts) : GetScopedConstsTable(); ConstGenVisitor constVisitor(_vm, _fs, _ctx, *this); SQObjectPtr value; constVisitor.process(decl->value(), value); constantsTbl->NewSlot(id, value); } void CodeGenVisitor::visitEnumDecl(EnumDecl *enums) { addLineNumber(enums); SQObjectPtr table(SQTable::Create(_fs->_sharedstate,0)); table._flags = SQOBJ_FLAG_IMMUTABLE; SQObjectPtr id = _fs->CreateString(enums->name()); CheckDuplicateLocalIdentifier(enums, id, "Enum", enums->isGlobal() && !(_fs->lang_features & LF_FORBID_GLOBAL_CONST_REWRITE)); for (auto &c : enums->consts()) { SQObjectPtr key = _fs->CreateString(c.id); _table(table)->NewSlot(key, convertLiteral(c.val)); } SQTable *enumsTable = enums->isGlobal() ? _table(_ss(_vm)->_consts) : GetScopedConstsTable(); enumsTable->NewSlot(id, SQObjectPtr(table)); } void CodeGenVisitor::MoveIfCurrentTargetIsLocal() { SQInteger trg = _fs->TopTarget(); if (_fs->IsLocal(trg)) { trg = _fs->PopTarget(); //pops the target and moves it _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), trg); } } void CodeGenVisitor::maybeAddInExprLine(Expr *expr) { if (!_ss(_vm)->_lineInfoInExpressions) return; if (expr->lineStart() != -1) { _fs->AddLineInfos(expr->lineStart(), true, false); } } void CodeGenVisitor::addLineNumber(Node *node) { SQInteger line = node->lineStart(); if (line != -1) { _fs->AddLineInfos(line, true, false); } } static bool isCalleeAnObject(const Expr *expr) { if (expr->isAccessExpr()) return expr->asAccessExpr()->receiver()->op() != TO_BASE; if (expr->op() == TO_ROOT_TABLE_ACCESS) return true; return false; } static bool isCalleeAnOuter(SQFuncState *_fs, const Expr *expr, SQInteger &outer_pos) { if (expr->op() != TO_ID) return false; SQObjectPtr nameObj(_fs->CreateString(expr->asId()->name())); SQCompiletimeVarInfo varInfo; if (_fs->GetLocalVariable(nameObj, varInfo) != -1) return false; outer_pos = _fs->GetOuterVariable(nameObj, varInfo); return outer_pos != -1; } void CodeGenVisitor::visitCallExpr(CallExpr *call) { maybeAddInExprLine(call); Expr *callee = deparen(call->callee()); bool isNullCall = call->isNullable(); bool isTypeMethod = false; if (callee->op() == TO_GETFIELD) { isTypeMethod = callee->asGetField()->isTypeMethod(); } visitForTarget(callee); SQInteger outerPos = -1; if (isCalleeAnObject(callee)) { if (!isNullCall && !isTypeMethod && ((_fs->lang_features & LF_FORBID_IMPLICIT_TYPE_METHODS) == 0)) { SQInteger key = _fs->PopTarget(); /* location of the key */ SQInteger table = _fs->PopTarget(); /* location of the object */ SQInteger closure = _fs->PushTarget(); /* location for the closure */ SQInteger ttarget = _fs->PushTarget(); /* location for 'this' pointer */ _fs->AddInstruction(_OP_PREPCALL, closure, key, table, ttarget); } else { SQInteger self = _fs->GetUpTarget(1); /* location of the object */ SQInteger storedSelf = _fs->PushTarget(); _fs->_optimization = false; _fs->AddInstruction(_OP_MOVE, storedSelf, self); _fs->PopTarget(); SQInteger flags = 0; if (isTypeMethod) { flags |= OP_GET_FLAG_TYPE_METHODS_ONLY; } else if ((_fs->lang_features & LF_FORBID_IMPLICIT_TYPE_METHODS) == 0) { flags |= OP_GET_FLAG_ALLOW_TYPE_METHODS; } if (isNullCall) flags |= OP_GET_FLAG_NO_ERROR; SQInteger key = _fs->PopTarget(); SQInteger src = _fs->PopTarget(); _fs->AddInstruction(_OP_GET, _fs->PushTarget(), key, src, flags); SQInteger ttarget = _fs->PushTarget(); _fs->_optimization = false; _fs->AddInstruction(_OP_MOVE, ttarget, storedSelf); } } else if (isCalleeAnOuter(_fs, callee, outerPos)) { _fs->AddInstruction(_OP_GETOUTER, _fs->PushTarget(), outerPos); _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0); } else { _fs->AddInstruction(_OP_MOVE, _fs->PushTarget(), 0); } const ArenaVector &args = call->arguments(); for (Expr *arg : args) { visitMaybeStaticMemo(arg); MoveIfCurrentTargetIsLocal(); } for (SQUnsignedInteger i = 0; i < args.size(); ++i) { _fs->PopTarget(); } SQInteger stackbase = _fs->PopTarget(); SQInteger closure = _fs->PopTarget(); SQInteger target = _fs->PushTarget(); assert(target >= -1); assert(target < 255); _fs->AddInstruction(isNullCall ? _OP_NULLCALL : _OP_CALL, target, closure, stackbase, args.size() + 1); } void CodeGenVisitor::visitBaseExpr(BaseExpr *base) { maybeAddInExprLine(base); _fs->AddInstruction(_OP_GETBASE, _fs->PushTarget()); } void CodeGenVisitor::visitRootTableAccessExpr(RootTableAccessExpr *expr) { maybeAddInExprLine(expr); _fs->AddInstruction(_OP_LOADROOT, _fs->PushTarget()); } void CodeGenVisitor::visitLiteralExpr(LiteralExpr *lit) { maybeAddInExprLine(lit); switch (lit->kind()) { case LK_STRING: _fs->AddInstruction(_OP_LOAD, _fs->PushTarget(), _fs->GetConstant(_fs->CreateString(lit->s()))); break; case LK_FLOAT:EmitLoadConstFloat(lit->f(), -1); break; case LK_INT: EmitLoadConstInt(lit->i(), -1); break; case LK_BOOL: _fs->AddInstruction(_OP_LOADBOOL, _fs->PushTarget(), lit->b()); break; case LK_NULL: _fs->AddInstruction(_OP_LOADNULLS, _fs->PushTarget(), 1); break; } } void CodeGenVisitor::visitArrayExpr(ArrayExpr *expr) { maybeAddInExprLine(expr); const auto &inits = expr->initializers(); _fs->AddInstruction(_OP_NEWOBJ, _fs->PushTarget(), inits.size(), 0, NEWOBJ_ARRAY); for (SQUnsignedInteger i = 0; i < inits.size(); ++i) { Expr *valExpr = inits[i]; #if SQ_LINE_INFO_IN_STRUCTURES if (i < 100 && valExpr->lineStart() != -1) _fs->AddLineInfos(valExpr->lineStart(), false, false); #endif visitForValueMaybeStaticMemo(valExpr); SQInteger val = _fs->PopTarget(); SQInteger array = _fs->TopTarget(); _fs->AddInstruction(_OP_APPENDARRAY, array, val, AAT_STACK); } } void CodeGenVisitor::emitUnaryOp(SQOpcode op, UnExpr *u) { Expr *arg = u->argument(); visitForValueMaybeStaticMemo(arg); if (_fs->_targetstack.size() == 0) reportDiagnostic(u, DiagnosticsId::DI_CANNOT_EVAL_UNARY); SQInteger src = _fs->PopTarget(); _fs->AddInstruction(op, _fs->PushTarget(), src); } void CodeGenVisitor::emitStaticMemo(Expr *static_memo_arg, bool is_auto_memo) { if (static_memo_arg->op() == TO_STATIC_MEMO) { emitStaticMemo(static_cast(static_memo_arg)->argument()); return; } bool old_inside_static_memo = _inside_static_memo; _inside_static_memo = true; SQInteger staticIdx = _fs->_staticmemos_count++; if (staticIdx > STATIC_MEMO_IDX_MASK) reportDiagnostic(static_memo_arg, DiagnosticsId::DI_GENERAL_COMPILE_ERROR, "too many static memos in function"); _fs->AddInstruction(_OP_DATA_NOP); // will be replaced by _OP_LOAD_STATIC_MEMO SQInteger loadInstrPos = _fs->GetCurrentPos(); visitForValue(static_memo_arg); if (_fs->_targetstack.size() == 0) reportDiagnostic(static_memo_arg, DiagnosticsId::DI_CANNOT_EVAL_UNARY); SQInteger src = _fs->PopTarget(); SQInteger dst = _fs->PushTarget(); SQInteger storeInstrPos = _fs->GetCurrentPos() + 1; SQInteger offset = storeInstrPos - loadInstrPos; if (offset >= 0xFFFF) reportDiagnostic(static_memo_arg, DiagnosticsId::DI_TOO_BIG_STATIC_MEMO); SQInteger encodedIdx = is_auto_memo ? (staticIdx | STATIC_MEMO_AUTO_FLAG) : staticIdx; _fs->SetInstructionParams(loadInstrPos, dst, encodedIdx, (offset) >> 8, (offset) & 0xFF); _fs->AddInstruction(_OP_SAVE_STATIC_MEMO, src, encodedIdx, (offset + 1) >> 8, (offset + 1) & 0xFF); _inside_static_memo = old_inside_static_memo; } void CodeGenVisitor::emitInlineConst(Expr *const_initializer) { ConstGenVisitor constVisitor(_vm, _fs, _ctx, *this); SQObjectPtr value; constVisitor.process(const_initializer, value); selectConstant(_fs->PushTarget(), value); } void CodeGenVisitor::emitDelete(UnExpr *ud) { Expr *argument = ud->argument(); visitForTarget(argument); switch (argument->op()) { case TO_GETFIELD: case TO_GETSLOT: break; case TO_BASE: reportDiagnostic(ud, DiagnosticsId::DI_CANNOT_DELETE, "'base'"); break; case TO_ID: reportDiagnostic(ud, DiagnosticsId::DI_CANNOT_DELETE, "an (outer) local"); break; default: reportDiagnostic(ud, DiagnosticsId::DI_CANNOT_DELETE, "an expression"); break; } SQInteger table = _fs->PopTarget(); //src in OP_GET SQInteger key = _fs->PopTarget(); //key in OP_GET _fs->AddInstruction(_OP_DELETE, _fs->PushTarget(), key, table); } void CodeGenVisitor::visitUnExpr(UnExpr *unary) { maybeAddInExprLine(unary); switch (unary->op()) { case TO_NEG: emitUnaryOp(_OP_NEG, unary); break; case TO_NOT: emitUnaryOp(_OP_NOT, unary); break; case TO_BNOT:emitUnaryOp(_OP_BWNOT, unary); break; case TO_TYPEOF: emitUnaryOp(_OP_TYPEOF, unary); break; case TO_RESUME: emitUnaryOp(_OP_RESUME, unary); break; case TO_CLONE: emitUnaryOp(_OP_CLONE, unary); break; case TO_PAREN: unary->argument()->visit(this); break; case TO_DELETE: emitDelete(unary); break; case TO_STATIC_MEMO: { #if STATIC_MEMO_ENABLED int score = getSubtreeConstScore(unary->argument(), true); if (score == 0) reportDiagnostic(_variable_node ? _variable_node : unary, DiagnosticsId::DI_NOT_A_CONST); if (score > 0 && score < 3) reportDiagnostic(_variable_node ? _variable_node : unary, DiagnosticsId::DI_STATIC_MEMO_TOO_SIMPLE); emitStaticMemo(unary->argument()); #else unary->argument()->visit(this); #endif break; } case TO_INLINE_CONST: emitInlineConst(unary->argument()); break; default: assert(!"Invalid unary op"); break; } } void CodeGenVisitor::emitSimpleBinaryOp(SQOpcode op, Expr *lhs, Expr *rhs, SQInteger op3) { visitForValueMaybeStaticMemo(lhs); visitForValueMaybeStaticMemo(rhs); SQInteger op1 = _fs->PopTarget(); SQInteger op2 = _fs->PopTarget(); _fs->AddInstruction(op, _fs->PushTarget(), op1, op2, op3); } void CodeGenVisitor::emitShortCircuitLogicalOp(SQOpcode op, Expr *lhs, Expr *rhs) { visitForValue(lhs); SQInteger first_exp = _fs->PopTarget(); SQInteger trg = _fs->PushTarget(); _fs->AddInstruction(op, trg, 0, first_exp, 0); SQInteger jpos = _fs->GetCurrentPos(); if (trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp); visitForValue(rhs); _fs->SnoozeOpt(); SQInteger second_exp = _fs->PopTarget(); if (trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp); _fs->SnoozeOpt(); _fs->SetInstructionParam(jpos, 1, (_fs->GetCurrentPos() - jpos)); } void CodeGenVisitor::emitCompoundArith(SQOpcode op, SQInteger opcode, Expr *lvalue, Expr *rvalue) { if (lvalue->isAccessExpr() && lvalue->asAccessExpr()->isFieldAccessExpr()) { FieldAccessExpr *fieldAccess = lvalue->asAccessExpr()->asFieldAccessExpr(); SQObjectPtr nameObj = _fs->CreateString(fieldAccess->fieldName()); SQInteger constantI = _fs->GetConstant(nameObj); if (constantI < 256u) { visitForValue(fieldAccess->receiver()); _fs->PushTarget(); visitForValueMaybeStaticMemo(rvalue); SQInteger val = _fs->PopTarget(); _fs->PopTarget(); SQInteger src = _fs->PopTarget(); /* _OP_COMPARITH mixes dest obj and source val in the arg1 */ _fs->AddInstruction(_OP_COMPARITH_K, _fs->PushTarget(), (src << 16) | val, constantI, opcode); return; } } visitForTarget(lvalue); visitForValueMaybeStaticMemo(rvalue); if (lvalue->op() == TO_ID) { Id *id = lvalue->asId(); SQObjectPtr nameObj(_fs->CreateString(id->name())); SQCompiletimeVarInfo varInfo; SQInteger pos = -1; if ((pos = _fs->GetLocalVariable(nameObj, varInfo)) != -1) { if ((varInfo.var_flags & VF_ASSIGNABLE) == 0) reportDiagnostic(lvalue, DiagnosticsId::DI_ASSIGN_TO_BINDING, id->name()); SQInteger p2 = _fs->PopTarget(); //src in OP_GET SQInteger p1 = _fs->PopTarget(); //key in OP_GET _fs->PushTarget(p1); //EmitCompArithLocal(tok, p1, p1, p2); _fs->AddInstruction(op, p1, p2, p1, 0); EmitCheckType(p1, varInfo.type_mask); _fs->SnoozeOpt(); } else if ((pos = _fs->GetOuterVariable(nameObj, varInfo)) != -1) { if ((varInfo.var_flags & VF_ASSIGNABLE) == 0) reportDiagnostic(lvalue, DiagnosticsId::DI_ASSIGN_TO_BINDING, id->name()); SQInteger val = _fs->TopTarget(); SQInteger tmp = _fs->PushTarget(); _fs->AddInstruction(_OP_GETOUTER, tmp, pos); _fs->AddInstruction(op, tmp, val, tmp, 0); EmitCheckType(tmp, varInfo.type_mask); _fs->PopTarget(); _fs->PopTarget(); _fs->AddInstruction(_OP_SETOUTER, _fs->PushTarget(), pos, tmp); } else { reportDiagnostic(lvalue, DiagnosticsId::DI_ASSIGN_TO_EXPR); } } else if (lvalue->isAccessExpr()) { SQInteger val = _fs->PopTarget(); SQInteger key = _fs->PopTarget(); SQInteger src = _fs->PopTarget(); /* _OP_COMPARITH mixes dest obj and source val in the arg1 */ _fs->AddInstruction(_OP_COMPARITH, _fs->PushTarget(), (src << 16) | val, key, opcode); } else { reportDiagnostic(lvalue, DiagnosticsId::DI_ASSIGN_TO_EXPR); } } void CodeGenVisitor::emitNewSlot(Expr *lvalue, Expr *rvalue) { if (lvalue->isAccessExpr() && lvalue->asAccessExpr()->receiver()->op() != TO_BASE && canBeLiteral(lvalue->asAccessExpr())) { FieldAccessExpr *fieldAccess = lvalue->asAccessExpr()->asFieldAccessExpr(); SQObjectPtr nameObj = _fs->CreateString(fieldAccess->fieldName()); SQInteger constantI = _fs->GetConstant(nameObj); //if (constantI < 256) // currently stored in arg1, unlimited { visitForValue(fieldAccess->receiver()); visitForValueMaybeStaticMemo(rvalue); SQInteger val = _fs->PopTarget(); SQInteger src = _fs->PopTarget(); _fs->AddInstruction(_OP_NEWSLOTK, _fs->PushTarget(), constantI, src, val); return; } } visitForTarget(lvalue); visitForValueMaybeStaticMemo(rvalue); if (lvalue->isAccessExpr()) { // d.f || d["f"] SQInteger val = _fs->PopTarget(); SQInteger key = _fs->PopTarget(); SQInteger src = _fs->PopTarget(); _fs->AddInstruction(_OP_NEWSLOT, _fs->PushTarget(), key, src, val); } else { reportDiagnostic(lvalue, DiagnosticsId::DI_LOCAL_SLOT_CREATE); } } void CodeGenVisitor::emitFieldAssign(int isLiteralIndex) { const bool isLiteral = isLiteralIndex >= 0; SQInteger val = _fs->PopTarget(); SQInteger key = isLiteralIndex >= 0 ? isLiteralIndex : _fs->PopTarget(); SQInteger src = _fs->PopTarget(); assert(src < 256); _fs->AddInstruction(isLiteral ? _OP_SET_LITERAL : _OP_SET, _fs->PushTarget(), key, src, val); static_assert(_OP_DATA_NOP == 0); if (isLiteral) _fs->AddInstruction(SQOpcode(0), 0, 0, 0, 0);//hint } void CodeGenVisitor::emitAssign(Expr *lvalue, Expr * rvalue) { if (lvalue->isAccessExpr() && lvalue->asAccessExpr()->receiver()->op() != TO_BASE && canBeLiteral(lvalue->asAccessExpr())) { FieldAccessExpr *fieldAccess = lvalue->asAccessExpr()->asFieldAccessExpr(); SQObjectPtr nameObj = _fs->CreateString(fieldAccess->fieldName()); SQInteger constantI = _fs->GetConstant(nameObj); // arg1 is of int size, enough visitForValue(fieldAccess->receiver()); visitForValueMaybeStaticMemo(rvalue); emitFieldAssign(constantI); return; } visitForTarget(lvalue); visitForValueMaybeStaticMemo(rvalue); if (lvalue->op() == TO_ID) { Id *id = lvalue->asId(); SQObjectPtr nameObj(_fs->CreateString(id->name())); SQCompiletimeVarInfo varInfo; SQInteger pos = -1; if ((pos = _fs->GetLocalVariable(nameObj, varInfo)) != -1) { if ((varInfo.var_flags & VF_ASSIGNABLE) == 0) reportDiagnostic(lvalue, DiagnosticsId::DI_ASSIGN_TO_BINDING, id->name()); SQInteger src = _fs->PopTarget(); SQInteger dst = _fs->TopTarget(); _fs->AddInstruction(_OP_MOVE, dst, src); if (!checkInferredType(lvalue, rvalue, varInfo.type_mask)) EmitCheckType(dst, varInfo.type_mask); } else if ((pos = _fs->GetOuterVariable(nameObj, varInfo)) != -1) { if ((varInfo.var_flags & VF_ASSIGNABLE) == 0) reportDiagnostic(lvalue, DiagnosticsId::DI_ASSIGN_TO_BINDING, id->name()); SQInteger src = _fs->PopTarget(); _fs->AddInstruction(_OP_SETOUTER, _fs->PushTarget(), pos, src); if (!checkInferredType(lvalue, rvalue, varInfo.type_mask)) EmitCheckType(src, varInfo.type_mask); } else { reportDiagnostic(lvalue, DiagnosticsId::DI_ASSIGN_TO_EXPR); } } else if (lvalue->isAccessExpr()) { if (lvalue->asAccessExpr()->receiver()->op() != TO_BASE) { emitFieldAssign(-1); } else { reportDiagnostic(lvalue, DiagnosticsId::DI_ASSIGN_TO_EXPR); } } else { reportDiagnostic(lvalue, DiagnosticsId::DI_ASSIGN_TO_EXPR); } } bool CodeGenVisitor::canBeLiteral(AccessExpr *expr) { if (!expr->isFieldAccessExpr()) return false; FieldAccessExpr *field = expr->asFieldAccessExpr(); return field->canBeLiteral(CanBeTypeMethod(field->fieldName())); } bool CodeGenVisitor::CanBeTypeMethod(const char *key) { if (_fs->lang_features & LF_FORBID_IMPLICIT_TYPE_METHODS) return false; // Check if key exists in any built-in type class // This can be optimized by keeping joined list/table of used keys SQClass *builtinClasses[] = { _class(_fs->_sharedstate->_null_class), _class(_fs->_sharedstate->_integer_class), _class(_fs->_sharedstate->_float_class), _class(_fs->_sharedstate->_bool_class), _class(_fs->_sharedstate->_table_class), _class(_fs->_sharedstate->_array_class), _class(_fs->_sharedstate->_string_class), _class(_fs->_sharedstate->_generator_class), _class(_fs->_sharedstate->_function_class), _class(_fs->_sharedstate->_thread_class), _class(_fs->_sharedstate->_class_class), _class(_fs->_sharedstate->_instance_class), _class(_fs->_sharedstate->_weakref_class), _class(_fs->_sharedstate->_userdata_class) }; SQObjectPtr tmp; int keyLen = strlen(key); for (SQInteger i = 0; i < sizeof(builtinClasses) / sizeof(builtinClasses[0]); ++i) { if (builtinClasses[i]->GetStr(key, keyLen, tmp)) return true; } return false; } SQObjectPtr CodeGenVisitor::GetTypeMethod(SQInteger sq_type, const char* key) { SQObjectPtr res; SQClass *builtinClass = nullptr; switch (sq_type) { case OT_NULL: builtinClass = _class(_fs->_sharedstate->_null_class); break; case OT_INTEGER: builtinClass = _class(_fs->_sharedstate->_integer_class); break; case OT_FLOAT: builtinClass = _class(_fs->_sharedstate->_float_class); break; case OT_BOOL: builtinClass = _class(_fs->_sharedstate->_bool_class); break; case OT_TABLE: builtinClass = _class(_fs->_sharedstate->_table_class); break; case OT_ARRAY: builtinClass = _class(_fs->_sharedstate->_array_class); break; case OT_STRING: builtinClass = _class(_fs->_sharedstate->_string_class); break; case OT_GENERATOR: builtinClass = _class(_fs->_sharedstate->_generator_class); break; case OT_CLOSURE: case OT_NATIVECLOSURE: builtinClass = _class(_fs->_sharedstate->_function_class); break; case OT_THREAD: builtinClass = _class(_fs->_sharedstate->_thread_class); break; case OT_CLASS: builtinClass = _class(_fs->_sharedstate->_class_class); break; case OT_INSTANCE: builtinClass = _class(_fs->_sharedstate->_instance_class); break; case OT_WEAKREF: builtinClass = _class(_fs->_sharedstate->_weakref_class); break; case OT_USERDATA: builtinClass = _class(_fs->_sharedstate->_userdata_class); break; case OT_USERPOINTER: // no type methods for user pointers case OT_FUNCPROTO: // no type methods for function prototypes return res; default: return res; } if (!builtinClass) return res; builtinClass->GetStr(key, strlen(key), res); return res; } bool CodeGenVisitor::CanBeTableTypeMethod(const char *key) { if (_fs->lang_features & LF_FORBID_IMPLICIT_TYPE_METHODS) return false; SQObjectPtr tmp; return _class(_fs->_sharedstate->_table_class)->GetStr(key, strlen(key), tmp); } void CodeGenVisitor::selectConstant(SQInteger target, const SQObjectPtr &constant) { SQObjectType ctype = sq_type(constant); switch (ctype) { case OT_INTEGER: EmitLoadConstInt(_integer(constant), target); break; case OT_FLOAT: EmitLoadConstFloat(_float(constant), target); break; case OT_BOOL: _fs->AddInstruction(_OP_LOADBOOL, target, _integer(constant)); break; case OT_NULL: _fs->AddInstruction(_OP_LOADNULLS, target, 1); break; default: _fs->AddInstruction(_OP_LOAD, target, _fs->GetConstant(constant)); break; } } void CodeGenVisitor::visitGetFieldExpr(GetFieldExpr *expr) { maybeAddInExprLine(expr); Expr *receiver = expr->receiver(); // Handle enums if (receiver->op() == TO_ID) { SQObjectPtr constant; SQObjectPtr id = _fs->CreateString(receiver->asId()->name()); if (IsConstant(id, constant)) { if (sq_type(constant) == OT_TABLE && (sq_objflags(constant) & SQOBJ_FLAG_IMMUTABLE) && !expr->isNullable()) { SQObjectPtr next; if (_table(constant)->GetStr(expr->fieldName(), strlen(expr->fieldName()), next)) { SQObjectType fieldType = sq_type(next); bool needsCallContext = fieldType == OT_CLOSURE || fieldType == OT_NATIVECLOSURE || fieldType == OT_GENERATOR || fieldType == OT_CLASS || fieldType == OT_INSTANCE; if (!needsCallContext) { selectConstant(_fs->PushTarget(), next); return; // Done with enum } // else fall through to normal get } else { if (!CanBeTableTypeMethod(expr->fieldName())) { reportDiagnostic(expr, DiagnosticsId::DI_INVALID_ENUM, expr->fieldName(), "enum"); return; } // else fall through to normal get } } } } visitForValue(receiver); SQObjectPtr nameObj = _fs->CreateString(expr->fieldName()); SQInteger constantI = _fs->GetConstant(nameObj); SQInteger constTarget = _fs->PushTarget(); SQInteger flags = expr->isNullable() ? OP_GET_FLAG_NO_ERROR : 0; bool typeMethod = CanBeTypeMethod(expr->fieldName()); if (typeMethod) { flags |= OP_GET_FLAG_ALLOW_TYPE_METHODS; } if (expr->isTypeMethod()) { flags |= OP_GET_FLAG_TYPE_METHODS_ONLY; } if (expr->receiver()->op() == TO_BASE) { _fs->PopTarget(); // key SQInteger src = _fs->PopTarget(); _fs->AddInstruction(_OP_GETK, _fs->PushTarget(), constantI, src, flags); } else if (_resolve_mode == ExprChainResolveMode::Value) { _fs->PopTarget(); // key SQInteger src = _fs->PopTarget(); if (expr->canBeLiteral(typeMethod)) { _fs->AddInstruction(_OP_GET_LITERAL, _fs->PushTarget(), constantI, src, flags); static_assert(_OP_DATA_NOP == 0); _fs->AddInstruction(SQOpcode(0), 0, 0, 0, 0); //hint } else { _fs->AddInstruction(_OP_GETK, _fs->PushTarget(), constantI, src, flags); } } else { _fs->AddInstruction(_OP_LOAD, constTarget, constantI); } } void CodeGenVisitor::visitGetSlotExpr(GetSlotExpr *expr) { // TODO: support similar optimization with contant table as in GetFieldExpr here too maybeAddInExprLine(expr); visitForValue(expr->receiver()); visitForValueMaybeStaticMemo(expr->key()); if (expr->receiver()->op() == TO_BASE || _resolve_mode == ExprChainResolveMode::Value) { SQInteger key = _fs->PopTarget(); //key in OP_GET SQInteger src = _fs->PopTarget(); //src in OP_GET _fs->AddInstruction(_OP_GET, _fs->PushTarget(), key, src, expr->isNullable() ? OP_GET_FLAG_NO_ERROR : 0); } } static inline bool is_literal_in_int_range(const Expr *expr) { if ( expr->op() != TO_LITERAL) return false; LiteralExpr * le = expr->asLiteral(); if (le->kind() != LK_INT) return false; SQInteger i = le->i(); return i >= SQInteger(INT_MIN) && i <= SQInteger(INT_MAX); } void CodeGenVisitor::visitBinExpr(BinExpr *expr) { maybeAddInExprLine(expr); switch (expr->op()) { case TO_NEWSLOT: emitNewSlot(expr->lhs(), expr->rhs()); break; case TO_NULLC: emitShortCircuitLogicalOp(_OP_NULLCOALESCE, expr->lhs(), expr->rhs()); break; case TO_OROR: emitShortCircuitLogicalOp(_OP_OR, expr->lhs(), expr->rhs()); break; case TO_ANDAND: emitShortCircuitLogicalOp(_OP_AND, expr->lhs(), expr->rhs()); break; case TO_ASSIGN: emitAssign(expr->lhs(), expr->rhs()); break; case TO_PLUSEQ: emitCompoundArith(_OP_ADD, '+', expr->lhs(), expr->rhs()); break; case TO_MINUSEQ: emitCompoundArith(_OP_SUB, '-', expr->lhs(), expr->rhs()); break; case TO_MULEQ: emitCompoundArith(_OP_MUL, '*', expr->lhs(), expr->rhs()); break; case TO_DIVEQ: emitCompoundArith(_OP_DIV, '/', expr->lhs(), expr->rhs()); break; case TO_MODEQ: emitCompoundArith(_OP_MOD, '%', expr->lhs(), expr->rhs()); break; case TO_ADD: if ( is_literal_in_int_range(expr->lhs()) ) { visitForValue(expr->rhs()); SQInteger op2 = _fs->PopTarget(); _fs->AddInstruction(_OP_ADDI, _fs->PushTarget(), expr->lhs()->asLiteral()->i(), op2, 0); } else if ( is_literal_in_int_range(expr->rhs()) ) { visitForValue(expr->lhs()); SQInteger op2 = _fs->PopTarget(); _fs->AddInstruction(_OP_ADDI, _fs->PushTarget(), expr->rhs()->asLiteral()->i(), op2, 0); } else emitSimpleBinaryOp(_OP_ADD, expr->lhs(), expr->rhs()); break; case TO_SUB: if ( is_literal_in_int_range(expr->rhs()) && expr->rhs()->asLiteral()->i() != SQInteger(INT_MIN) ) { visitForValue(expr->lhs()); SQInteger op2 = _fs->PopTarget(); _fs->AddInstruction(_OP_ADDI, _fs->PushTarget(), -expr->rhs()->asLiteral()->i(), op2, 0); } else emitSimpleBinaryOp(_OP_SUB, expr->lhs(), expr->rhs()); break; case TO_MUL: emitSimpleBinaryOp(_OP_MUL, expr->lhs(), expr->rhs()); break; case TO_DIV: emitSimpleBinaryOp(_OP_DIV, expr->lhs(), expr->rhs()); break; case TO_MOD: emitSimpleBinaryOp(_OP_MOD, expr->lhs(), expr->rhs()); break; case TO_OR: emitSimpleBinaryOp(_OP_BITW, expr->lhs(), expr->rhs(), BW_OR); break; case TO_AND: emitSimpleBinaryOp(_OP_BITW, expr->lhs(), expr->rhs(), BW_AND); break; case TO_XOR: emitSimpleBinaryOp(_OP_BITW, expr->lhs(), expr->rhs(), BW_XOR); break; case TO_USHR:emitSimpleBinaryOp(_OP_BITW, expr->lhs(), expr->rhs(), BW_USHIFTR); break; case TO_SHR: emitSimpleBinaryOp(_OP_BITW, expr->lhs(), expr->rhs(), BW_SHIFTR); break; case TO_SHL: emitSimpleBinaryOp(_OP_BITW, expr->lhs(), expr->rhs(), BW_SHIFTL); break; case TO_EQ: emitSimpleBinaryOp(_OP_EQ, expr->lhs(), expr->rhs()); break; case TO_NE: emitSimpleBinaryOp(_OP_NE, expr->lhs(), expr->rhs()); break; case TO_GE: emitSimpleBinaryOp(_OP_CMP, expr->lhs(), expr->rhs(), CMP_GE); break; case TO_GT: emitSimpleBinaryOp(_OP_CMP, expr->lhs(), expr->rhs(), CMP_G); break; case TO_LE: emitSimpleBinaryOp(_OP_CMP, expr->lhs(), expr->rhs(), CMP_LE); break; case TO_LT: emitSimpleBinaryOp(_OP_CMP, expr->lhs(), expr->rhs(), CMP_L); break; case TO_3CMP: emitSimpleBinaryOp(_OP_CMP, expr->lhs(), expr->rhs(), CMP_3W); break; case TO_IN: emitSimpleBinaryOp(_OP_EXISTS, expr->lhs(), expr->rhs()); break; case TO_INSTANCEOF: emitSimpleBinaryOp(_OP_INSTANCEOF, expr->lhs(), expr->rhs()); break; default: assert(!"Unknown binary expression"); break; } } void CodeGenVisitor::visitTerExpr(TerExpr *expr) { maybeAddInExprLine(expr); assert(expr->op() == TO_TERNARY); visitForValue(expr->a()); _fs->AddInstruction(_OP_JZ, _fs->PopTarget()); SQInteger jzpos = _fs->GetCurrentPos(); SQInteger trg = _fs->PushTarget(); visitForValueMaybeStaticMemo(expr->b()); SQInteger first_exp = _fs->PopTarget(); if (trg != first_exp) _fs->AddInstruction(_OP_MOVE, trg, first_exp); SQInteger endfirstexp = _fs->GetCurrentPos(); _fs->AddInstruction(_OP_JMP, 0, 0); SQInteger jmppos = _fs->GetCurrentPos(); visitForValueMaybeStaticMemo(expr->c()); SQInteger second_exp = _fs->PopTarget(); if (trg != second_exp) _fs->AddInstruction(_OP_MOVE, trg, second_exp); _fs->SetInstructionParam(jmppos, 1, _fs->GetCurrentPos() - jmppos); _fs->SetInstructionParam(jzpos, 1, endfirstexp - jzpos + 1); _fs->SnoozeOpt(); } void CodeGenVisitor::visitIncExpr(IncExpr *expr) { maybeAddInExprLine(expr); Expr *arg = expr->argument(); visitForTarget(arg); if ((arg->op() == TO_GETFIELD || arg->op() == TO_GETSLOT) && arg->asAccessExpr()->receiver()->op() == TO_BASE) { reportDiagnostic(arg, DiagnosticsId::DI_INC_DEC_NOT_ASSIGNABLE); } bool isPostfix = expr->form() == IF_POSTFIX; if (arg->isAccessExpr()) { SQInteger p2 = _fs->PopTarget(); SQInteger p1 = _fs->PopTarget(); _fs->AddInstruction(isPostfix ? _OP_PINC : _OP_INC, _fs->PushTarget(), p1, p2, expr->diff()); } else if (arg->op() == TO_ID) { Id *id = arg->asId(); SQObjectPtr nameObj(_fs->CreateString(id->name())); SQInteger pos = -1; SQCompiletimeVarInfo varInfo; if ((pos = _fs->GetLocalVariable(nameObj, varInfo)) != -1) { if ((varInfo.var_flags & VF_ASSIGNABLE) == 0) reportDiagnostic(arg, DiagnosticsId::DI_INC_DEC_NOT_ASSIGNABLE); SQInteger src = isPostfix ? _fs->PopTarget() : _fs->TopTarget(); SQInteger dst = isPostfix ? _fs->PushTarget() : src; _fs->AddInstruction(isPostfix ? _OP_PINCL : _OP_INCL, dst, src, 0, expr->diff()); } else if ((pos = _fs->GetOuterVariable(nameObj, varInfo)) != -1) { if ((varInfo.var_flags & VF_ASSIGNABLE) == 0) reportDiagnostic(arg, DiagnosticsId::DI_INC_DEC_NOT_ASSIGNABLE); SQInteger tmp1 = _fs->PushTarget(); SQInteger tmp2 = isPostfix ? _fs->PushTarget() : tmp1; _fs->AddInstruction(_OP_GETOUTER, tmp2, pos); _fs->AddInstruction(_OP_PINCL, tmp1, tmp2, 0, expr->diff()); _fs->AddInstruction(_OP_SETOUTER, tmp2, pos, tmp2); if (isPostfix) { _fs->PopTarget(); } } else { reportDiagnostic(arg, DiagnosticsId::DI_INC_DEC_NOT_ASSIGNABLE); } } else { reportDiagnostic(arg, DiagnosticsId::DI_INC_DEC_NOT_ASSIGNABLE); } } void CodeGenVisitor::visitDirectiveStatement(DirectiveStmt *directive) { _fs->lang_features = (_fs->lang_features | directive->setFlags) & ~directive->clearFlags; if (directive->applyToDefault) _ss(_vm)->defaultLangFeatures = (_ss(_vm)->defaultLangFeatures | directive->setFlags) & ~directive->clearFlags; } bool CodeGenVisitor::IsConstant(const SQObject &name, SQObjectPtr &e) { if (IsLocalConstant(name, e)) return true; if (IsGlobalConstant(name, e)) return true; return false; } bool CodeGenVisitor::IsLocalConstant(const SQObject &name, SQObjectPtr &e) { SQObjectPtr val; for (SQInteger i = SQInteger(_scopedconsts.size()) - 1; i >= 0; --i) { SQObjectPtr &tbl = _scopedconsts[i]; if (!sq_isnull(tbl) && _table(tbl)->Get(SQObjectPtr(name), val)) { e = val; if (tbl._flags & SQOBJ_FLAG_IMMUTABLE) e._flags |= SQOBJ_FLAG_IMMUTABLE; return true; } } return false; } bool CodeGenVisitor::IsGlobalConstant(const SQObject &name, SQObjectPtr &e) { SQObjectPtr val; if (_table(_ss(_vm)->_consts)->Get(SQObjectPtr(name), val)) { e = val; return true; } return false; } bool CodeGenVisitor::isConstEvaluable(Expr *expr) { switch (expr->op()) { case TO_PAREN: case TO_INLINE_CONST: return isConstEvaluable(static_cast(expr)->argument()); //-V1037 case TO_LITERAL: return true; case TO_ID: { SQObjectPtr name = _fs->CreateString(expr->asId()->name()); SQCompiletimeVarInfo varInfo; if (_fs->GetLocalVariable(name, varInfo) != -1 || _fs->GetOuterVariable(name, varInfo) != -1) return false; // local/outer shadows any constant SQObjectPtr c; return IsConstant(name, c); } case TO_NEG: case TO_NOT: case TO_BNOT: case TO_TYPEOF: return isConstEvaluable(static_cast(expr)->argument()); //-V1037 case TO_ADD: case TO_SUB: case TO_MUL: case TO_DIV: case TO_MOD: case TO_OR: case TO_AND: case TO_XOR: case TO_SHL: case TO_SHR: case TO_USHR: case TO_EQ: case TO_NE: case TO_LT: case TO_LE: case TO_GT: case TO_GE: case TO_3CMP: case TO_OROR: case TO_ANDAND: case TO_NULLC: case TO_IN: case TO_INSTANCEOF: { BinExpr *bin = static_cast(expr); return isConstEvaluable(bin->lhs()) && isConstEvaluable(bin->rhs()); } case TO_TERNARY: { TerExpr *ter = static_cast(expr); return isConstEvaluable(ter->a()) && isConstEvaluable(ter->b()) && isConstEvaluable(ter->c()); } case TO_GETFIELD: return isConstEvaluable(expr->asGetField()->receiver()); case TO_GETSLOT: { GetSlotExpr *gs = expr->asGetSlot(); return isConstEvaluable(gs->receiver()) && isConstEvaluable(gs->key()); } default: return false; } } void CodeGenVisitor::visitCommaExpr(CommaExpr *expr) { for (auto e : expr->expressions()) { visitForValue(e); } } void CodeGenVisitor::visitId(Id *id) { maybeAddInExprLine(id); SQInteger pos = -1; SQObjectPtr constant; SQObjectPtr idObj = _fs->CreateString(id->name()); SQCompiletimeVarInfo varInfo; if (sq_isstring(_fs->_name) && strcmp(_stringval(_fs->_name), id->name()) == 0 && _fs->GetLocalVariable(_fs->_name, varInfo) == -1) { _fs->AddInstruction(_OP_LOADCALLEE, _fs->PushTarget()); return; } if (_string(idObj) == _string(_fs->_name)) { reportDiagnostic(id, DiagnosticsId::DI_CONFLICTS_WITH, "variable", id->name(), "function name"); } if ((pos = _fs->GetLocalVariable(idObj, varInfo)) != -1) { _fs->PushTarget(pos); } else if ((pos = _fs->GetOuterVariable(idObj, varInfo)) != -1) { if (_resolve_mode == ExprChainResolveMode::Value) { SQInteger stkPos = _fs->PushTarget(); _fs->AddInstruction(_OP_GETOUTER, stkPos, pos); } } else if (IsConstant(idObj, constant)) { /* Handle named constant */ SQObjectPtr constval(constant); SQInteger stkPos = _fs->PushTarget(); /* generate direct or literal function depending on size */ SQObjectType ctype = sq_type(constval); switch (ctype) { case OT_INTEGER: EmitLoadConstInt(_integer(constval), stkPos); break; case OT_FLOAT: EmitLoadConstFloat(_float(constval), stkPos); break; case OT_BOOL: _fs->AddInstruction(_OP_LOADBOOL, stkPos, _integer(constval)); break; case OT_NULL: _fs->AddInstruction(_OP_LOADNULLS, stkPos, 1); break; default: _fs->AddInstruction(_OP_LOAD, stkPos, _fs->GetConstant(constval)); break; } } else { reportDiagnostic(id, DiagnosticsId::DI_UNKNOWN_SYMBOL, id->name()); } } void CodeGenVisitor::visitForTarget(Node *n) { ExprChainResolveMode old_mode = _resolve_mode; _resolve_mode = ExprChainResolveMode::Target; n->visit(this); _resolve_mode = old_mode; } void CodeGenVisitor::visitForValue(Node *n) { ExprChainResolveMode old_mode = _resolve_mode; _resolve_mode = ExprChainResolveMode::Value; n->visit(this); _resolve_mode = old_mode; } bool CodeGenVisitor::visitMaybeStaticMemo(Node *n) { // AST-level constant folding: fold compound const expressions to a single LOAD if (GLOBAL_OPTIMIZATION_SWITCH && n->isExpression()) { Expr *expr = n->asExpression(); TreeOp exprOp = expr->op(); // Skip leaves -- already handled optimally by their own visitors if (exprOp != TO_LITERAL && exprOp != TO_ID && isConstEvaluable(expr)) { emitInlineConst(expr); return true; } } bool arraysAndTablesFreezed = (_fs->lang_features & LF_ALLOW_AUTO_FREEZE) != 0; int score = (n->isExpression() && !_inside_static_memo) ? getSubtreeConstScore(n->asExpression(), arraysAndTablesFreezed) : 0; if (STATIC_MEMO_ENABLED && GLOBAL_OPTIMIZATION_SWITCH && score > STATIC_MEMO_CONST_SCORE_THRESHOLD && n->op() != TO_STATIC_MEMO && !(_fs->lang_features & LF_DISABLE_OPTIMIZER) && _complexity_level > 0) { //reportDiagnostic(n, DiagnosticsId::DI_FORGOT_SUBST); //printf("static memo applied const score=%d pos:%d:%d\n", getSubtreeConstScore(n->asExpression(), false), n->lineStart(), n->columnStart()); emitStaticMemo(n->asExpression(), /*is_auto_memo=*/true); } else { if ((_fs->lang_features & LF_ALLOW_AUTO_FREEZE) != 0 && (n->op() == TO_ARRAY || n->op() == TO_TABLE || n->op() == TO_CLASS)) { visitForValue(n); SQInteger src = _fs->PopTarget(); _fs->AddInstruction(_OP_FREEZE, _fs->PushTarget(), src); } else visitForValue(n); } return score > 0; } bool CodeGenVisitor::visitForValueMaybeStaticMemo(Node *n) { ExprChainResolveMode old_mode = _resolve_mode; _resolve_mode = ExprChainResolveMode::Value; bool res = visitMaybeStaticMemo(n); _resolve_mode = old_mode; return res; } } // namespace SQCompilation #endif ================================================ FILE: squirrel/compiler/codegen.h ================================================ #pragma once #include "ast.h" #include "opcodes.h" #include "compilationcontext.h" struct SQFuncState; namespace SQCompilation { struct SQScope { SQInteger outers; SQInteger stacksize; }; class CodeGenVisitor : public Visitor { SQFuncState *_fs; SQFuncState *_childFs; SQVM *_vm; SQScope _scope; SQObjectPtrVec _scopedconsts; // Handling of chain slot reference expressions such as `a.b.c` or `a[b][c]`. enum class ExprChainResolveMode { Value, // Fully resolve and fetch the value now (emit GET/GETOUTER opcodes, push constants, etc.). Target // Do not fetch; leave the chain in a writable/callable form (e.g., receiver+key or outer ref) }; ExprChainResolveMode _resolve_mode; bool _inside_static_memo; int _complexity_level; const char *_sourceName; Arena *_arena; // This is used to pass variable stack positions from visitVarDecl() to visitDestructuringDecl() // This is somewhat implicit and should be redone. SQInteger _last_declared_var_pos = -1; SQCompilationContext &_ctx; public: CodeGenVisitor(Arena *arena, const HSQOBJECT *bindings, SQVM *vm, const char *sourceName, SQCompilationContext &ctx); bool generate(RootBlock *root, SQObjectPtr &out); // API for const building bool IsConstant(const SQObject &name, SQObjectPtr &e); bool IsLocalConstant(const SQObject &name, SQObjectPtr &e); bool IsGlobalConstant(const SQObject &name, SQObjectPtr &e); SQObjectPtr compileConstFunc(FunctionExpr *funcExpr); private: void reportDiagnostic(Node *n, int32_t id, ...); void CheckDuplicateLocalIdentifier(Node *n, SQObject name, const char *desc, bool ignore_global_consts); bool CheckMemberUniqueness(ArenaVector &vec, Expr *obj); void EmitLoadConstInt(SQInteger value, SQInteger target); void EmitLoadConstFloat(SQFloat value, SQInteger target); void ResolveBreaks(SQFuncState *funcstate, SQInteger ntoresolve); void ResolveContinues(SQFuncState *funcstate, SQInteger ntoresolve, SQInteger targetpos); void EmitDerefOp(SQOpcode op); void EmitCheckType(int target, uint32_t type_mask); void generateTableExpr(TableExpr *tableExpr); SQTable* GetScopedConstsTable(); void SaveDocstringToVM(void *key, const DocObject &docObject); void emitUnaryOp(SQOpcode op, UnExpr *arg); void emitDelete(UnExpr *argument); void emitSimpleBinaryOp(SQOpcode op, Expr *lhs, Expr *rhs, SQInteger op3 = 0); void emitShortCircuitLogicalOp(SQOpcode op, Expr *lhs, Expr *rhs); void emitCompoundArith(SQOpcode op, SQInteger opcode, Expr *lvalue, Expr *rvalue); void emitStaticMemo(Expr *static_memo_arg, bool is_auto_memo = false); void emitInlineConst(Expr *const_initializer); bool _visit_arrays_and_tables; Node *_variable_node; int getSubtreeConstScore(Node *node, bool visit_arrays_and_tables); int getSubtreeConstScoreImpl(Node *node); bool findConstScoredVar(const SQObjectPtr &name, SQCompiletimeVarInfo &varInfo, unsigned requiredFlags); SQInteger inferReceiverType(Expr *receiver); bool isConstScoredMethodCall(GetFieldExpr *getField); bool isConstScoredDirectCall(CallExpr *callExpr); bool isConstEvaluable(Expr *expr); void emitNewSlot(Expr *lvalue, Expr *rvalue); void emitAssign(Expr *lvalue, Expr * rvalue); void emitFieldAssign(int isLiteralIndex); bool CanBeTypeMethod(const char *key); bool CanBeTableTypeMethod(const char *key); bool canBeLiteral(AccessExpr *expr); SQObjectPtr GetTypeMethod(SQInteger sq_type, const char* key); void MoveIfCurrentTargetIsLocal(); bool IsSimpleConstant(const SQObject &name); bool DoesObjectContainOnlySimpleObjects(const SQObject &obj, int depth); bool IsGuaranteedPureFunction(Node *expr); SQObjectPtr convertLiteral(LiteralExpr *lit); void maybeAddInExprLine(Expr *expr); void addLineNumber(Node *node); void visitForTarget(Node *n); void visitForValue(Node *n); bool visitMaybeStaticMemo(Node *n); bool visitForValueMaybeStaticMemo(Node *n); void selectConstant(SQInteger target, const SQObjectPtr &constant); void addPatchDocObjectInstruction(const DocObject &docObject); unsigned inferExprTypeMask(Expr *expr); unsigned inferExprTypeMaskImpl(Expr *expr); bool checkInferredType(Node *reportNode, Expr *expr, unsigned declaredMask); Expr *deparen(Expr *e) const; bool isFreezeCall(Expr *node); Expr *skipConstFreezePure(Expr *expr); bool isPureFunctionCall(Expr *node); SQObjectPtr compileFunc(FunctionExpr *funcExpr, bool is_const, sqvector *out_defparam_values); public: void visitBlock(Block *block) override; void visitCodeBlockExpr(CodeBlockExpr *expr) override; void visitIfStatement(IfStatement *ifStmt) override; void visitWhileStatement(WhileStatement *whileLoop) override; void visitDoWhileStatement(DoWhileStatement *doWhileLoop) override; void visitForStatement(ForStatement *forLoop) override; void visitForeachStatement(ForeachStatement *foreachLoop) override; void visitSwitchStatement(SwitchStatement *swtch) override; void visitTryStatement(TryStatement *tryStmt) override; void visitBreakStatement(BreakStatement *breakStmt) override; void visitContinueStatement(ContinueStatement *continueStmt) override; void visitTerminateStatement(TerminateStatement *terminator) override; void visitReturnStatement(ReturnStatement *retStmt) override; void visitYieldStatement(YieldStatement *yieldStmt) override; void visitThrowStatement(ThrowStatement *throwStmt) override; void visitExprStatement(ExprStatement *stmt) override; void visitTableExpr(TableExpr *tableExpr) override; void visitClassExpr(ClassExpr *klass) override; void visitParamDecl(ParamDecl *param) override; void visitVarDecl(VarDecl *var) override; void visitDeclGroup(DeclGroup *group) override; void visitDestructuringDecl(DestructuringDecl *destruct) override; void visitFunctionExpr(FunctionExpr *func) override; void visitConstDecl(ConstDecl *decl) override; void visitEnumDecl(EnumDecl *enums) override; void visitCallExpr(CallExpr *call) override; void visitBaseExpr(BaseExpr *base) override; void visitRootTableAccessExpr(RootTableAccessExpr *expr) override; void visitLiteralExpr(LiteralExpr *lit) override; void visitArrayExpr(ArrayExpr *expr) override; void visitUnExpr(UnExpr *unary) override; void visitGetFieldExpr(GetFieldExpr *expr) override; void visitGetSlotExpr(GetSlotExpr *expr) override; void visitBinExpr(BinExpr *expr) override; void visitTerExpr(TerExpr *expr) override; void visitIncExpr(IncExpr *expr) override; void visitId(Id *id) override; void visitCommaExpr(CommaExpr *expr) override; void visitDirectiveStatement(DirectiveStmt *dir) override; }; } // namespace SQCompilation ================================================ FILE: squirrel/compiler/compilationcontext.cpp ================================================ #include #include #include #include "compilationcontext.h" #include "sqstring.h" #include "keyValueFile.h" #include namespace SQCompilation { enum DiagnosticSubsystem { DSS_LEX, DSS_SYNTAX, DSS_SEMA, DSS_COMPILETIME, }; struct DiagnosticDescriptor { const char *format; const enum DiagnosticSeverity severity; const enum DiagnosticSubsystem subsystem; const int32_t id; const char *textId; bool disabled; }; static const char severityPrefixes[] = "hwe"; static const char *severityNames[] = { "HINT", "WARNING", "ERROR", nullptr }; static DiagnosticDescriptor diagnosticDescriptors[] = { #define DEF_DIAGNOSTIC(_, severity, subsytem, num_id, text_id, fmt) { fmt, DS_##severity, DSS_##subsytem, num_id, text_id, false } DIAGNOSTICS #undef DEF_DIAGNOSTIC }; SQCompilationContext::SQCompilationContext(SQVM *vm, Arena *arena, const char *sn, const char *code, size_t csize, const Comments *comments, bool raiseError) : _vm(vm) , _arena(arena) , _sourceName(sn) , _linemap(_ss(vm)->_alloc_ctx) , _comments(comments) , _code(code) , _codeSize(csize) , _raiseError(raiseError) { } SQCompilationContext::~SQCompilationContext() { } Comments::~Comments() { SQAllocContext ctx = _commentsList._alloc_ctx; for (auto &lc : _commentsList) { for (auto &c : lc) { sq_vm_free(ctx, (void *)c.text, c.size + 1); } } } const Comments::LineCommentsList &Comments::lineComment(int line) const { if (_commentsList.empty()) return emptyComments; line -= 1; assert(0 <= line && line < _commentsList.size()); return _commentsList[line]; } void SQCompilationContext::buildLineMap() { assert(_code != NULL); int prev = '\n'; for (size_t i = 0; i < _codeSize; ++i) { if (prev == '\n') { _linemap.push_back(i); } prev = _code[i]; } } const char *SQCompilationContext::findLine(int lineNo) { if (_linemap.empty()) buildLineMap(); lineNo -= 1; if (lineNo < 0 || lineNo >= _linemap.size()) return nullptr; return _code + _linemap[lineNo]; } void SQCompilationContext::printAllWarnings(FILE *ostream) { for (auto &diag : diagnosticDescriptors) { if (diag.severity == DS_ERROR) continue; fprintf(ostream, "w%d (%s)\n", diag.id, diag.textId); fprintf(ostream, diag.format, "***", "***", "***", "***", "***", "***", "***", "***"); fprintf(ostream, "\n\n"); } } bool SQCompilationContext::enableWarning(const char *diagName, bool state) { for (auto &diag : diagnosticDescriptors) { if (strcmp(diagName, diag.textId) == 0) { if (diag.severity != DS_ERROR) { diag.disabled = !state; } return true; } } return false; } bool SQCompilationContext::enableWarning(int32_t id, bool state) { for (auto &diag : diagnosticDescriptors) { if (id == diag.id) { if (diag.severity != DS_ERROR) { diag.disabled = !state; } return true; } } return false; } void SQCompilationContext::switchSyntaxWarningsState(bool state) { for (auto &diag : diagnosticDescriptors) { if (diag.severity == DS_WARNING && (diag.subsystem == DSS_SYNTAX || diag.subsystem == DSS_LEX || diag.subsystem == DSS_COMPILETIME)) { diag.disabled = !state; } } } bool SQCompilationContext::isRequireDisabled(int line, int col) { if (!_comments) return false; auto &lineCmts = _comments->lineComment(line); for (auto &comment : lineCmts) { if (strstr(comment.text, "-skip-require")) return true; } return false; } bool SQCompilationContext::isDisabled(enum DiagnosticsId id, int line, int pos) { DiagnosticDescriptor &descriptor = diagnosticDescriptors[id]; if (descriptor.severity >= DS_ERROR) return false; if (descriptor.disabled) return true; if (!_comments) return false; const Comments::LineCommentsList &lineCmts = _comments->lineComment(line); char suppressLineIntBuf[64] = { 0 }; char suppressLineTextBuf[128] = { 0 }; snprintf(suppressLineIntBuf, sizeof(suppressLineIntBuf), "-%c%d", severityPrefixes[descriptor.severity], descriptor.id); snprintf(suppressLineTextBuf, sizeof(suppressLineTextBuf), "-%s", descriptor.textId); for (auto &comment : lineCmts) { if (strstr(comment.text, suppressLineIntBuf)) return true; if (strstr(comment.text, suppressLineTextBuf)) return true; } char suppressFileIntBuf[64] = { 0 }; char suppressFileTextBuf[128] = { 0 }; snprintf(suppressFileIntBuf, sizeof(suppressFileIntBuf), "-file:%c%d", severityPrefixes[descriptor.severity], descriptor.id); snprintf(suppressFileTextBuf, sizeof(suppressFileTextBuf), "-file:%s", descriptor.textId); for (auto &lineComments : _comments->commentsList()) { for (auto &comment : lineComments) { if (strstr(comment.text, suppressFileIntBuf)) return true; if (strstr(comment.text, suppressFileTextBuf)) return true; } } return false; } static void drawUnderliner(int32_t column, int32_t width, std::string &msg) { int32_t i = 0; while (i < column) { msg.push_back(' '); ++i; } ++i; msg.push_back('^'); while ((i - column) < width) { msg.push_back('-'); ++i; } } bool SQCompilationContext::isBlankLine(const char *l) { if (!l) return true; while ((l < _code + _codeSize) && *l && *l != '\n') { if (!sq_isspace(*l)) return false; ++l; } return true; } void SQCompilationContext::renderDiagnosticHeader(enum DiagnosticsId diag, std::string *msg, ...) { va_list vargs; va_start(vargs, msg); vrenderDiagnosticHeader(diag, *msg, vargs); va_end(vargs); } void SQCompilationContext::vrenderDiagnosticHeader(enum DiagnosticsId diag, std::string &message, va_list vargs) { auto &desc = diagnosticDescriptors[diag]; char tempBuffer[2048] = { 0 }; snprintf(tempBuffer, sizeof tempBuffer, "%s: ", severityNames[desc.severity]); message.append(tempBuffer); if (desc.id > 0) { snprintf(tempBuffer, sizeof tempBuffer, "%c%d (%s) ", severityPrefixes[desc.severity], desc.id, desc.textId); message.append(tempBuffer); } vsnprintf(tempBuffer, sizeof tempBuffer, desc.format, vargs); message.append(tempBuffer); } void SQCompilationContext::vreportDiagnostic(enum DiagnosticsId diagId, int32_t line, int32_t pos, int32_t width, va_list vargs) { assert(diagId < DI_NUM_OF_DIAGNOSTICS); bool terminate = false; if (!isDisabled(diagId, line, pos)) { auto &desc = diagnosticDescriptors[diagId]; bool isError = desc.severity >= DS_ERROR; std::string message; vrenderDiagnosticHeader(diagId, message, vargs); std::string extraInfo; const char *l1 = findLine(line - 1); const char *l2 = findLine(line); const char *l3 = findLine(line + 1); const char *lastSourceCodeChar = _code + _codeSize - 1; if (l1 != nullptr && !isBlankLine(l1)) { extraInfo.push_back('\n'); int32_t j = 0; while ((l1 + j <= lastSourceCodeChar) && l1[j] && l1[j] != '\n' && l1[j] != '\r') { //-V522 extraInfo.push_back(l1[j++]); //-V595 } } if (l2 != nullptr) { extraInfo.push_back('\n'); int32_t j = 0; while ((l2 + j <= lastSourceCodeChar) && l2[j] && l2[j] != '\n' && l2[j] != '\r') { //-V522 extraInfo.push_back(l2[j++]); //-V595 } extraInfo.push_back('\n'); j = 0; drawUnderliner(pos, width, extraInfo); } if (l3 != nullptr && !isBlankLine(l3)) { extraInfo.push_back('\n'); int32_t j = 0; while ((l3 + j <= lastSourceCodeChar) && l3[j] && l3[j] != '\n' && l3[j] != '\r') { //-V522 extraInfo.push_back(l3[j++]); //-V595 } } const char *extra = nullptr; if (l1 || l2 || l3) { extraInfo.push_back('\n'); extraInfo.push_back('\n'); // separate with extra line extra = extraInfo.c_str(); } auto messageFunc = _ss(_vm)->_compilererrorhandler; const char *msg = message.c_str(); auto diagMsgFunc = _ss(_vm)->_compilerdiaghandler; if (diagMsgFunc) { SQCompilerMessage cm; cm.intId = desc.id; cm.textId = desc.textId; cm.line = line; cm.column = pos; cm.columnsWidth = width; cm.message = msg; cm.fileName = _sourceName; cm.isError = isError; diagMsgFunc(_vm, &cm); } if (_raiseError && messageFunc) { SQMessageSeverity sev = SEV_ERROR; if (desc.severity == DS_HINT) sev = SEV_HINT; else if (desc.severity == DS_WARNING) sev = SEV_WARNING; messageFunc(_vm, sev, msg, _sourceName, line, pos, extra); } if (isError) { _vm->_lasterror = SQString::Create(_ss(_vm), msg, message.length()); terminate = true; } } if (terminate) throw CompilerError(); } void SQCompilationContext::reportDiagnostic(enum DiagnosticsId diagId, int32_t line, int32_t pos, int32_t width, ...) { va_list vargs; va_start(vargs, width); vreportDiagnostic(diagId, line, pos, width, vargs); va_end(vargs); } } // namespace SQCompilation ================================================ FILE: squirrel/compiler/compilationcontext.h ================================================ #pragma once #include #include #include #include #include "squirrel.h" #include "sqvm.h" #include "sqstate.h" #include "squtils.h" #include "arena.h" class KeyValueFile; /* Do we need identifiers for errors? Unlike warninggs, errors have no id (numeric or symbolic), cannot be turned on and off, cannot be of type other than ERROR. So instead of reportDiagnostic(), we may just have throwError() function with printf-like args and this would break the compilation process immediately. And reportDiagnostic() would do just like it does now - only report warnings. For convenience, adding GENERAL_COMPILE_ERROR for now - no need to add new identifiers to use it in a single place. Note how there are not benefits in separate ids for errors over GENERAL_COMPILE_ERROR - it already does all what is needed. */ #define DIAGNOSTICS \ DEF_DIAGNOSTIC(GENERAL_COMPILE_ERROR, ERROR, LEX, -1, "", "%s"), \ DEF_DIAGNOSTIC(LITERAL_OVERFLOW, ERROR, LEX, -1, "", "%s constant overflow"), \ DEF_DIAGNOSTIC(LITERAL_UNDERFLOW, ERROR, LEX, -1, "", "%s constant underflow"), \ DEF_DIAGNOSTIC(FP_EXP_EXPECTED, ERROR, LEX, -1, "", "exponent expected"), \ DEF_DIAGNOSTIC(MALFORMED_NUMBER, ERROR, LEX, -1, "", "malformed number"), \ DEF_DIAGNOSTIC(HEX_DIGITS_EXPECTED, ERROR, LEX, -1, "", "expected hex digits after '0x'"), \ DEF_DIAGNOSTIC(HEX_TOO_MANY_DIGITS, ERROR, LEX, -1, "", "too many digits for a hex number"), \ DEF_DIAGNOSTIC(OCTAL_NOT_SUPPORTED, ERROR, LEX, -1, "", "leading 0 is not allowed, octal numbers are not supported"), \ DEF_DIAGNOSTIC(TOO_LONG_LITERAL, ERROR, LEX, -1, "", "constant too long"), \ DEF_DIAGNOSTIC(EMPTY_LITERAL, ERROR, LEX, -1, "", "empty constant"), \ DEF_DIAGNOSTIC(UNRECOGNISED_ESCAPER, ERROR, LEX, -1, "", "unrecognised escape char"), \ DEF_DIAGNOSTIC(NEWLINE_IN_CONST, ERROR, LEX, -1, "", "newline in a constant"), \ DEF_DIAGNOSTIC(UNFINISHED_STRING, ERROR, LEX, -1, "", "unfinished string"), \ DEF_DIAGNOSTIC(HEX_NUMBERS_EXPECTED, ERROR, LEX, -1, "", "hexadecimal number expected"), \ DEF_DIAGNOSTIC(UNEXPECTED_CHAR, ERROR, LEX, -1, "", "unexpected character '%s'"), \ DEF_DIAGNOSTIC(INVALID_TOKEN, ERROR, LEX, -1, "", "invalid token '%s'"), \ DEF_DIAGNOSTIC(LEX_ERROR_PARSE, ERROR, LEX, -1, "", "error parsing %s"), \ DEF_DIAGNOSTIC(BRACE_ORDER, ERROR, LEX, -1, "", "in brace order"), \ DEF_DIAGNOSTIC(NO_PARAMS_IN_STRING_TEMPLATE, ERROR, LEX, -1, "", "no params collected for string interpolation"), \ DEF_DIAGNOSTIC(COMMENT_IN_STRING_TEMPLATE, ERROR, LEX, -1, "", "comments inside interpolated string are not supported"), \ DEF_DIAGNOSTIC(EXPECTED_LEX, ERROR, LEX, -1, "", "expected %s"), \ DEF_DIAGNOSTIC(MACRO_RECURSION, ERROR, LEX, -1, "", "recursion in reader macro"), \ DEF_DIAGNOSTIC(INVALID_CHAR, ERROR, LEX, -1, "", "Invalid character"), \ DEF_DIAGNOSTIC(TRAILING_BLOCK_COMMENT, ERROR, LEX, -1, "", "missing \"*/\" in comment"), \ DEF_DIAGNOSTIC(GLOBAL_CONSTS_ONLY, ERROR, SYNTAX, -1, "", "global can be applied to const and enum only"), \ DEF_DIAGNOSTIC(END_OF_STMT_EXPECTED, ERROR, SYNTAX, -1, "", "end of statement expected (; or lf)"), \ DEF_DIAGNOSTIC(EXPECTED_TOKEN, ERROR, SYNTAX, -1, "", "expected '%s'"), \ DEF_DIAGNOSTIC(INVALID_TYPE_NAME_SUGGESTION, ERROR, SYNTAX, -1, "", "Invalid type name '%s', did you mean '%s'?"), \ DEF_DIAGNOSTIC(INVALID_TYPE_NAME, ERROR, SYNTAX, -1, "", "Invalid type name '%s'"), \ DEF_DIAGNOSTIC(UNSUPPORTED_DIRECTIVE, ERROR, SYNTAX, -1, "", "unsupported directive '%s'"), \ DEF_DIAGNOSTIC(EXPECTED_LINENUM, ERROR, SYNTAX, -1, "", "expected line number after #pos:"), \ DEF_DIAGNOSTIC(EXPECTED_COLNUM, ERROR, SYNTAX, -1, "", "expected column number after #pos::"), \ DEF_DIAGNOSTIC(TOO_BIG_AST, ERROR, SYNTAX, -1, "", "AST too big. Consider simplifying it"), \ DEF_DIAGNOSTIC(TOO_BIG_STATIC_MEMO, ERROR, SYNTAX, -1, "", "static expression is too big"), \ DEF_DIAGNOSTIC(MULTIPLE_DOCSTRINGS, ERROR, SYNTAX, -1, "", "multiple docstrings in a single block"), \ DEF_DIAGNOSTIC(ASSIGN_INSIDE_FORBIDDEN, ERROR, SYNTAX, -1, "", "'=' inside '%s' is forbidden"), \ DEF_DIAGNOSTIC(BROKEN_SLOT_DECLARATION, ERROR, SYNTAX, -1, "", "cannot break deref/or comma needed after [exp]=exp slot declaration"), \ DEF_DIAGNOSTIC(ROOT_TABLE_FORBIDDEN, ERROR, SYNTAX, -1, "", "Access to root table is forbidden"), \ DEF_DIAGNOSTIC(DELETE_OP_FORBIDDEN, ERROR, SYNTAX, -1, "", "Usage of 'delete' operator is forbidden. Use 'o.rawdelete(\"key\")' instead"), \ DEF_DIAGNOSTIC(COMPILER_INTERNALS_FORBIDDEN, ERROR, SYNTAX, -1, "", "Access to compiler internals is forbidden"), \ DEF_DIAGNOSTIC(UNINITIALIZED_BINDING, ERROR, SEMA, -1, "", "Binding '%s' must be initialized"), \ DEF_DIAGNOSTIC(SAME_FOREACH_KV_NAMES, ERROR, SEMA, -1, "", "foreach() key and value names are the same: '%s'"), \ DEF_DIAGNOSTIC(SCALAR_EXPECTED, ERROR, SYNTAX, -1, "", "scalar expected : %s"), \ DEF_DIAGNOSTIC(VARARG_WITH_DEFAULT_ARG, ERROR, SYNTAX, -1, "", "function with default parameters cannot have variable number of parameters"), \ DEF_DIAGNOSTIC(LOOP_CONTROLLER_NOT_IN_LOOP, ERROR, SEMA, -1, "", "'%s' has to be in a loop block"), \ DEF_DIAGNOSTIC(ASSIGN_TO_EXPR, ERROR, SEMA, -1, "", "can't assign to expression"), \ DEF_DIAGNOSTIC(BASE_NOT_MODIFIABLE, ERROR, SEMA, -1, "", "'base' cannot be modified"), \ DEF_DIAGNOSTIC(ASSIGN_TO_BINDING, ERROR, SEMA, -1, "", "can't assign to binding '%s' (probably declaring using 'local' was intended, but 'let' was used)"), \ DEF_DIAGNOSTIC(LOCAL_SLOT_CREATE, ERROR, SEMA, -1, "", "can't 'create' a local slot"), \ DEF_DIAGNOSTIC(CANNOT_INC_DEC, ERROR, SEMA, -1, "", "can't '++' or '--' %s"), \ DEF_DIAGNOSTIC(VARNAME_CONFLICTS, ERROR, SEMA, -1, "", "Variable name %s conflicts with function name"), \ DEF_DIAGNOSTIC(ONLY_SINGLE_VARIABLE_DECLARATION, ERROR, SEMA, -1, "", "Only single variable declaration is allowed here"), \ DEF_DIAGNOSTIC(INITIALIZATION_REQUIRED, ERROR, SEMA, -1, "", "Initialization required"), \ DEF_DIAGNOSTIC(INVALID_ENUM, ERROR, SEMA, -1, "", "invalid enum [no '%s' field in '%s']"), \ DEF_DIAGNOSTIC(UNKNOWN_SYMBOL, ERROR, SEMA, -1, "", "Unknown variable [%s]"), \ DEF_DIAGNOSTIC(CANNOT_EVAL_UNARY, ERROR, SEMA, -1, "", "cannot evaluate unary-op"), \ DEF_DIAGNOSTIC(DUPLICATE_KEY, ERROR, SEMA, -1, "", "duplicate key '%s'"), \ DEF_DIAGNOSTIC(DUPLICATE_FUNC_ATTR, ERROR, SEMA, -1, "", "duplicate attribute '%s'"), \ DEF_DIAGNOSTIC(INVALID_SLOT_INIT, ERROR, SEMA, -1, "", "Invalid slot initializer '%s' - no such variable/constant or incorrect expression"), \ DEF_DIAGNOSTIC(CANNOT_DELETE, ERROR, SEMA, -1, "", "can't delete %s"), \ DEF_DIAGNOSTIC(CONFLICTS_WITH, ERROR, SEMA, -1, "", "%s name '%s' conflicts with %s"), \ DEF_DIAGNOSTIC(INC_DEC_NOT_ASSIGNABLE, ERROR, SEMA, -1, "", "argument of inc/dec operation is not assignable"), \ DEF_DIAGNOSTIC(TYPE_DIFFERS, ERROR, SEMA, -1, "", "%s type differs from the declared type"), \ DEF_DIAGNOSTIC(INFERRED_TYPE_MISMATCH, ERROR, SEMA, -1, "", "expression of type '%s' cannot be assigned to type '%s'"), \ DEF_DIAGNOSTIC(SPACE_SEP_FIELD_NAME, ERROR, SEMA, -1, "", "Separate access operator '%s' with its following name is forbidden"), \ DEF_DIAGNOSTIC(TOO_MANY_SYMBOLS, ERROR, SEMA, -1, "", "internal compiler error: too many %s"), \ DEF_DIAGNOSTIC(NOT_ALLOWED_IN_CONST, ERROR, SEMA, -1, "", "%s is not allowed in constant"), \ DEF_DIAGNOSTIC(ID_IS_NOT_CONST, ERROR, SEMA, -1, "", "Identifier '%s' is not a compile-time constant"), \ DEF_DIAGNOSTIC(CONSTANT_SLOT_NOT_FOUND, ERROR, SEMA, -1, "", "%s slot %s not found in constant object"), \ DEF_DIAGNOSTIC(CONSTANT_FIELD_NOT_FOUND, ERROR, SEMA, -1, "", "Field '%s' not found in constant object"), \ DEF_DIAGNOSTIC(PAREN_IS_FUNC_CALL, WARNING, SYNTAX, 190, "paren-is-function-call", "'(' on a new line parsed as function call."), \ DEF_DIAGNOSTIC(STMT_SAME_LINE, WARNING, SYNTAX, 192, "statement-on-same-line", "Next statement on the same line after '%s' statement."), \ DEF_DIAGNOSTIC(NULLABLE_OPERANDS, WARNING, SEMA, 200, "potentially-nulled-ops", "%s with potentially nullable expression."), \ DEF_DIAGNOSTIC(AND_OR_PAREN, WARNING, SEMA, 202, "and-or-paren", "Priority of the '&&' operator is higher than that of the '||' operator. Perhaps parentheses are missing?"), \ DEF_DIAGNOSTIC(BITWISE_BOOL_PAREN, WARNING, SEMA, 203, "bitwise-bool-paren", "Result of bitwise operation used in boolean expression. Perhaps parentheses are missing?"), \ DEF_DIAGNOSTIC(BITWISE_OP_TO_BOOL, WARNING, SEMA, 204, "bitwise-apply-to-bool", "The '&' or '|' operator is applied to boolean type. You've probably forgotten to include parentheses or intended to use the '&&' or '||' operator."), \ DEF_DIAGNOSTIC(UNREACHABLE_CODE, WARNING, SEMA, 205, "unreachable-code", "Unreachable code after '%s'."), \ DEF_DIAGNOSTIC(ASSIGNED_TWICE, WARNING, SEMA, 206, "assigned-twice", "Variable is assigned twice successively."), \ DEF_DIAGNOSTIC(NULLABLE_ASSIGNMENT, WARNING, SEMA, 208, "potentially-nulled-assign", "Assignment to potentially nullable expression."), \ DEF_DIAGNOSTIC(ASG_TO_ITSELF, WARNING, SEMA, 209, "assigned-to-itself", "The variable is assigned to itself."), \ DEF_DIAGNOSTIC(POTENTIALLY_NULLABLE_INDEX, WARNING, SEMA, 210, "potentially-nulled-index", "Potentially nullable expression used as array index."), \ DEF_DIAGNOSTIC(DUPLICATE_CASE, WARNING, SEMA, 211, "duplicate-case", "Duplicate case value."), \ DEF_DIAGNOSTIC(DUPLICATE_IF_EXPR, WARNING, SEMA, 212, "duplicate-if-expression", "Detected pattern 'if (A) {...} else if (A) {...}'. Branch unreachable."), \ DEF_DIAGNOSTIC(THEN_ELSE_EQUAL, WARNING, SEMA, 213, "then-and-else-equals", "'then' statement is equivalent to 'else' statement."), \ DEF_DIAGNOSTIC(TERNARY_SAME_VALUES, WARNING, SEMA, 214, "operator-returns-same-val", "Both branches of operator '<> ? <> : <>' are equivalent."), \ DEF_DIAGNOSTIC(TERNARY_PRIOR, WARNING, SEMA, 215, "ternary-priority", "The '?:' operator has lower priority than the '%s' operator. Perhaps the '?:' operator works in a different way than it was expected."), \ DEF_DIAGNOSTIC(SAME_OPERANDS, WARNING, SEMA, 216, "same-operands", "Left and right operands of '%s' operator are the same."), \ DEF_DIAGNOSTIC(UNCOND_TERMINATED_LOOP, WARNING, SEMA, 217, "unconditional-terminated-loop", "Unconditional '%s' inside a loop."), \ DEF_DIAGNOSTIC(POTENTIALLY_NULLABLE_CONTAINER, WARNING, SEMA, 220, "potentially-nulled-container", "'foreach' on potentially nullable expression."), \ DEF_DIAGNOSTIC(UNUTILIZED_EXPRESSION, WARNING, SEMA, 221, "result-not-utilized", "Result of operation is not used."), \ DEF_DIAGNOSTIC(BOOL_AS_INDEX, WARNING, SEMA, 222, "bool-as-index", "Boolean used as array index."), \ DEF_DIAGNOSTIC(COMPARE_WITH_BOOL, WARNING, SEMA, 223, "compared-with-bool", "Comparison with boolean."), \ DEF_DIAGNOSTIC(EMPTY_BODY, WARNING, SEMA, 224, "empty-body", "%s has an empty body."), \ DEF_DIAGNOSTIC(NOT_ALL_PATH_RETURN_VALUE, WARNING, SEMA, 225, "all-paths-return-value", "Not all control paths return a value."), \ DEF_DIAGNOSTIC(RETURNS_DIFFERENT_TYPES, WARNING, SEMA, 226, "return-different-types", "Function can return different types."), \ DEF_DIAGNOSTIC(ID_HIDES_ID, WARNING, SEMA, 227, "ident-hides-ident", "%s '%s' hides %s with the same name."), \ DEF_DIAGNOSTIC(DECLARED_NEVER_USED, WARNING, SEMA, 228, "declared-never-used", "%s '%s' was declared but never used."), \ DEF_DIAGNOSTIC(COPY_OF_EXPR, WARNING, SEMA, 229, "copy-of-expression", "Duplicate expression found inside the sequence of operations."), \ DEF_DIAGNOSTIC(IMPORTED_NEVER_USED, WARNING, SEMA, 230, "imported-never-used", "Imported field '%s' was never used."), \ DEF_DIAGNOSTIC(FORMAT_ARGUMENTS_COUNT, WARNING, SEMA, 231, "format-arguments-count", "Format string: arguments count mismatch."), \ DEF_DIAGNOSTIC(ALWAYS_T_OR_F, WARNING, SEMA, 232, "always-true-or-false", "Expression is always '%s'."), \ DEF_DIAGNOSTIC(CONST_IN_BOOL_EXPR, WARNING, SEMA, 233, "const-in-bool-expr", "Constant in a boolean expression."), \ DEF_DIAGNOSTIC(DIV_BY_ZERO, WARNING, SEMA, 234, "div-by-zero", "Integer division by zero."), \ DEF_DIAGNOSTIC(ROUND_TO_INT, WARNING, SEMA, 235, "round-to-int", "Result of division will be integer."), \ DEF_DIAGNOSTIC(SHIFT_PRIORITY, WARNING, SEMA, 236, "shift-priority", "Shift operator has lower priority. Perhaps parentheses are missing?"), \ DEF_DIAGNOSTIC(NAME_LIKE_SHOULD_RETURN, WARNING, SEMA, 238, "named-like-should-return", "Function name '%s' implies a return value, but its result is never used."), \ DEF_DIAGNOSTIC(NAME_RET_BOOL, WARNING, SEMA, 239, "named-like-return-bool", "Function name '%s' implies a return boolean type but not all control paths return boolean."), \ DEF_DIAGNOSTIC(NULL_COALESCING_PRIORITY, WARNING, SEMA, 240, "null-coalescing-priority", "The '??""' operator has a lower priority than the '%s' operator (a??b > c == a??""(b > c)). Perhaps the '??""' operator works in a different way than it was expected."), \ DEF_DIAGNOSTIC(ALREADY_REQUIRED, WARNING, SEMA, 241, "already-required", "Module '%s' has been required already."), \ DEF_DIAGNOSTIC(USED_FROM_STATIC, WARNING, SEMA, 244, "used-from-static", "Access 'this.%s' from %s function."), \ DEF_DIAGNOSTIC(ACCESS_POT_NULLABLE, WARNING, SEMA, 248, "access-potentially-nulled", "'%s' can be null, but is used as a %s without checking."), \ DEF_DIAGNOSTIC(CMP_WITH_CONTAINER, WARNING, SEMA, 250, "cmp-with-container", "Comparison with a %s."), \ DEF_DIAGNOSTIC(BOOL_PASSED_TO_STRANGE, WARNING, SEMA, 254, "bool-passed-to-strange", "Boolean passed to '%s' operator."), \ DEF_DIAGNOSTIC(DUPLICATE_FUNC, WARNING, SEMA, 255, "duplicate-function", "Duplicate function body. Consider functions '%s' and '%s'."), \ DEF_DIAGNOSTIC(KEY_NAME_MISMATCH, WARNING, SEMA, 256, "key-and-function-name", "Key and function name are not the same ('%s' and '%s')."), \ DEF_DIAGNOSTIC(DUPLICATE_ASSIGN_EXPR, WARNING, SEMA, 257, "duplicate-assigned-expr", "Duplicate of the assigned expression."), \ DEF_DIAGNOSTIC(SIMILAR_FUNC, WARNING, SEMA, 258, "similar-function", "Function bodies are very similar. Consider functions '%s' and '%s'."), \ DEF_DIAGNOSTIC(SIMILAR_ASSIGN_EXPR, WARNING, SEMA, 259, "similar-assigned-expr", "Assigned expression is very similar to one of the previous ones."), \ DEF_DIAGNOSTIC(NAME_EXPECTS_RETURN, WARNING, SEMA, 260, "named-like-must-return-result", "Function '%s' has name like it should return a value, but not all control paths return a value."), \ DEF_DIAGNOSTIC(SUSPICIOUS_FMT, WARNING, SYNTAX, 262, "suspicious-formatting", "Suspicious code formatting."), \ DEF_DIAGNOSTIC(EGYPT_BRACES, WARNING, SYNTAX, 263, "egyptian-braces", "Indentation style: 'egyptian braces' required."), \ DEF_DIAGNOSTIC(PLUS_STRING, WARNING, SEMA, 264, "plus-string", "Usage of '+' for string concatenation."), \ DEF_DIAGNOSTIC(FORGOTTEN_DO, WARNING, SEMA, 266, "forgotten-do", "'while' after the statement list (forgot 'do' ?)"), \ DEF_DIAGNOSTIC(SUSPICIOUS_BRACKET, WARNING, SYNTAX, 267, "suspicious-bracket", "'%s' will be parsed as '%s' (forgot ',' ?)"), \ DEF_DIAGNOSTIC(MIXED_SEPARATORS, WARNING, SYNTAX, 269, "mixed-separators", "Mixed spaces and commas to separate %s."), \ DEF_DIAGNOSTIC(EXTEND_TO_APPEND, HINT, SEMA, 270, "extent-to-append", "It is better to use 'append(A, B, ...)' instead of 'extend([A, B, ...])'."), \ DEF_DIAGNOSTIC(FORGOT_SUBST, WARNING, SEMA, 271, "forgot-subst", "'{}' found inside string (forgot 'subst' or '$' ?)."), \ DEF_DIAGNOSTIC(NOT_UNARY_OP, WARNING, SYNTAX, 272, "not-unary-op", "This '%s' is not a unary operator. Please use ' ' after it or ',' before it for better understandability."), \ DEF_DIAGNOSTIC(MISSED_BREAK, WARNING, SEMA, 275, "missed-break", "A 'break' statement is probably missing in a 'switch' statement."), \ DEF_DIAGNOSTIC(SPACE_AT_EOL, WARNING, LEX, 277, "space-at-eol", "Whitespace at the end of line."), \ DEF_DIAGNOSTIC(FORBIDDEN_FUNC, WARNING, SEMA, 278, "forbidden-function", "It is forbidden to call '%s' function."), \ DEF_DIAGNOSTIC(MISMATCH_LOOP_VAR, WARNING, SEMA, 279, "mismatch-loop-variable", "The variable used in for-loop does not match the initialized one."), \ DEF_DIAGNOSTIC(FORBIDDEN_PARENT_DIR, WARNING, SEMA, 280, "forbidden-parent-dir", "Access to the parent directory is forbidden in this function."), \ DEF_DIAGNOSTIC(UNWANTED_MODIFICATION, WARNING, SEMA, 281, "unwanted-modification", "Function '%s' modifies the object. You probably didn't want to modify the object here."), \ DEF_DIAGNOSTIC(USELESS_NULLC, WARNING, SEMA, 283, "useless-null-coalescing", "The expression to the right of the '??""' is null."), \ DEF_DIAGNOSTIC(CAN_BE_SIMPLIFIED, WARNING, SEMA, 284, "can-be-simplified", "Expression can be simplified."), \ DEF_DIAGNOSTIC(EXPR_NOT_NULL, WARNING, SEMA, 285, "expr-cannot-be-null", "The expression to the left of the '%s' cannot be null."), \ DEF_DIAGNOSTIC(DECL_IN_EXPR, WARNING, SEMA, 286, "decl-in-expression", "Declaration used in arith expression as operand."), \ DEF_DIAGNOSTIC(RANGE_CHECK, WARNING, SEMA, 287, "range-check", "It looks like the range boundaries are not checked correctly. Pay attention to checking with minimum and maximum index."), \ DEF_DIAGNOSTIC(PARAM_COUNT_MISMATCH, WARNING, SEMA, 288, "param-count", "Function '%s' (%d..%d parameters) is called with the wrong number of arguments (%d)."),\ DEF_DIAGNOSTIC(PARAM_POSITION_MISMATCH, WARNING, SEMA, 289, "param-pos", "The function parameter '%s' seems to be in the wrong position."), \ DEF_DIAGNOSTIC(INVALID_UNDERSCORE, WARNING, SEMA, 291, "invalid-underscore", "The name of %s '%s' is invalid. The identifier is marked as unused with a prefix underscore, but it is used."), \ DEF_DIAGNOSTIC(MODIFIED_CONTAINER, WARNING, SEMA, 292, "modified-container", "The container was modified within the loop."), \ DEF_DIAGNOSTIC(DUPLICATE_PERSIST_ID, WARNING, SEMA, 293, "duplicate-persist-id", "Duplicate id '%s' passed to 'persist'."), \ DEF_DIAGNOSTIC(UNDEFINED_GLOBAL, WARNING, SEMA, 295, "undefined-global", "Undefined global identifier '%s'."), \ DEF_DIAGNOSTIC(CALL_FROM_ROOT, WARNING, SEMA, 297, "call-from-root", "Function '%s' must be called from the root scope."), \ DEF_DIAGNOSTIC(INTEGER_OVERFLOW, WARNING, SEMA, 304, "integer-overflow", "Integer Overflow."), \ DEF_DIAGNOSTIC(RELATIVE_CMP_BOOL, WARNING, SEMA, 305, "relative-bool-cmp", "Relative comparison of non-boolean with boolean. It is a potential runtime error."), \ DEF_DIAGNOSTIC(EQ_PAREN_MISSED, WARNING, SEMA, 306, "eq-paren-miss", "Suspicious expression, probably parens are missing."), \ DEF_DIAGNOSTIC(GLOBAL_NAME_REDEF, WARNING, SEMA, 307, "global-id-redef", "Redefinition of existing global name '%s'."), \ DEF_DIAGNOSTIC(BOOL_LAMBDA_REQUIRED, WARNING, SEMA, 308, "bool-lambda-required", "Function '%s' requires lambda which returns boolean."), \ DEF_DIAGNOSTIC(MISSING_DESTRUCTURED_VALUE, WARNING, SEMA, 309, "missing-destructured-value", "No value for '%s' in destructured declaration."), \ DEF_DIAGNOSTIC(DESTRUCTURED_TYPE_MISMATCH, WARNING, SEMA, 310, "destructured-type", "Destructured type mismatch, right side is %s."), \ DEF_DIAGNOSTIC(WRONG_TYPE, WARNING, SEMA, 311, "wrong-type", "Wrong type: expected %s, got %s."), \ DEF_DIAGNOSTIC(MISSING_FIELD, WARNING, SEMA, 312, "missing-field", "No field '%s' in %s."), \ DEF_DIAGNOSTIC(MISSING_MODULE, HINT, SEMA, 313, "missing-module", "Module '%s' not found, or it returns null."), \ DEF_DIAGNOSTIC(SEE_OTHER, HINT, SEMA, 314, "see-other", "You can find %s here."), \ DEF_DIAGNOSTIC(INVALID_INDENTATION, WARNING, SYNTAX, 315, "invalid-indentation", "Invalid indentation. Pay attention to lines %s and %s."), \ DEF_DIAGNOSTIC(NOT_A_CONST, WARNING, COMPILETIME, 316, "not-a-const", "Expression in 'static' context must be a constant expression."), \ DEF_DIAGNOSTIC(STATIC_MEMO_TOO_SIMPLE, WARNING, COMPILETIME, 317, "static-too-simple", "'static' is too simple."), \ DEF_DIAGNOSTIC(MERGE_EMPTY_TABLE, WARNING, SEMA, 318, "merge-empty-table", "'__merge({})' with an empty table is equivalent to 'clone'. Use 'clone' instead."), \ DEF_DIAGNOSTIC(EMPTY_ARRAY_RESIZE, WARNING, SEMA, 319, "empty-array-resize", "'[].resize(...)' is slower than 'array(...)'. Use 'array(...)' instead."), \ DEF_DIAGNOSTIC(CALLBACK_SHOULD_RETURN_VALUE, WARNING, SEMA, 320, "callback-should-return-value", "Callback passed to '%s' must return a value."), \ DEF_DIAGNOSTIC(PARAM_ASSIGNMENT_IN_LAMBDA, WARNING, SEMA, 321, "param-assign-in-lambda", "Assignment to parameter '%s' in lambda has no effect. Return the expression instead."), \ DEF_DIAGNOSTIC(CALLBACK_SHOULD_NOT_RETURN, WARNING, SEMA, 322, "callback-should-not-return", "Callback passed to '%s' should not return a value.") \ namespace SQCompilation { struct CompilerError {}; // Exception to throw enum DiagnosticsId { #define DEF_DIAGNOSTIC(id, _, __, ___, _____, ______) DI_##id DIAGNOSTICS, #undef DEF_DIAGNOSTIC DI_NUM_OF_DIAGNOSTICS }; enum DiagnosticSeverity { DS_HINT, DS_WARNING, DS_ERROR }; enum CommentKind { CK_NONE, CK_LINE, // | // foo bar CK_BLOCK, // | /* foo bar */ CK_ML_BLOCK // | /* foo \n bar */ }; struct CommentData { CommentData() : kind(CK_NONE), size(0), text(nullptr), commentLine(-1), begin(-1), end(-1) {} CommentData(enum CommentKind k, size_t s, const char *t, SQInteger n, SQInteger b, SQInteger e) : kind(k), size(s), text(t), commentLine(n), begin(b), end(e) {} const enum CommentKind kind; const size_t size; const char *text; const SQInteger commentLine; // stand for line number in multiline block comment, or 0 otherwise const SQInteger begin, end; bool isLineComment() const { return kind == CK_LINE; } bool isBlockComment() const { return kind == CK_BLOCK; } bool isMultiLineComment() const { return kind == CK_ML_BLOCK; } }; class Comments { public: using LineCommentsList = sqvector; Comments(SQAllocContext ctx) : _commentsList(ctx), emptyComments(ctx) {} ~Comments(); const LineCommentsList &lineComment(int line) const; sqvector &commentsList() { return _commentsList; } const sqvector &commentsList() const { return _commentsList; } void pushNewLine() { _commentsList.push_back(LineCommentsList(_commentsList._alloc_ctx)); } private: sqvector _commentsList; LineCommentsList emptyComments; }; class SQCompilationContext { public: SQCompilationContext(SQVM *vm, Arena *a, const char *sn, const char *code, size_t csize, const Comments *comments, bool raiseError); ~SQCompilationContext(); const char *sourceName() const { return _sourceName; } SQAllocContext allocContext() const { return _ss(_vm)->_alloc_ctx; } SQVM *getVm() const { return _vm; } static void vrenderDiagnosticHeader(enum DiagnosticsId diag, std::string &msg, va_list args); static void renderDiagnosticHeader(enum DiagnosticsId diag, std::string *msg, ...); void vreportDiagnostic(enum DiagnosticsId diag, int32_t line, int32_t pos, int32_t width, va_list args); void reportDiagnostic(enum DiagnosticsId diag, int32_t line, int32_t pos, int32_t width, ...); bool isDisabled(enum DiagnosticsId id, int line, int pos); bool isRequireDisabled(int line, int col); static void printAllWarnings(FILE *ostream); static bool enableWarning(const char *diagName, bool state); static bool enableWarning(int32_t id, bool state); static void switchSyntaxWarningsState(bool state); Arena *arena() const { return _arena; } private: void buildLineMap(); const char *findLine(int lineNo); bool isBlankLine(const char *l); const char *_sourceName; SQVM *_vm; const char *_code; size_t _codeSize; Arena *_arena; bool _raiseError; sqvector _linemap; const Comments *_comments; }; } // namespace SQCompilation ================================================ FILE: squirrel/compiler/compiler.cpp ================================================ /* see copyright notice in squirrel.h */ #include "sqpcheader.h" #ifndef NO_COMPILER #include #include #include "opcodes.h" #include "sqstring.h" #include "sqfuncproto.h" #include "compiler.h" #include "sqfuncstate.h" #include "optimizer.h" #include "lexer.h" #include "sqvm.h" #include "sqtable.h" #include "ast.h" #include "parser.h" #include "codegen.h" #include "sqtypeparser.h" #include "optimizations/closureHoisting.h" #include "compilationcontext.h" #include "static_analyzer/analyzer.h" namespace SQCompilation { static RootBlock *parseASTImpl(Arena *astArena, SQVM *vm, const char *sourceText, size_t sourceTextSize, const char *sourcename, Comments *comments, bool raiseerror) { Arena parseArena(_ss(vm)->_alloc_ctx, "Parser"); SQCompilationContext ctx(vm, &parseArena, sourcename, sourceText, sourceTextSize, comments, raiseerror); SQParser p(vm, sourceText, sourceTextSize, sourcename, astArena, ctx, comments); RootBlock *r = p.parse(); if (r) { ClosureHoistingOpt opt(_ss(vm), astArena); opt.run(r); } return r; } SqASTData *ParseToAST(SQVM *vm, const char *sourceText, size_t sourceTextSize, const char *sourcename, bool preserveComments, bool raiseerror) { SqASTData *astData = sq_allocateASTData(vm); if (sourcename) { strncpy(astData->sourceName, sourcename, sizeof(astData->sourceName)); astData->sourceName[sizeof(astData->sourceName) / sizeof(astData->sourceName[0]) - 1] = 0; } astData->root = parseASTImpl(astData->astArena, vm, sourceText, sourceTextSize, astData->sourceName, preserveComments ? astData->comments : nullptr, raiseerror); if (astData->root) return astData; sq_releaseASTData(vm, astData); return nullptr; } bool Compile(SQVM *vm, const char *sourceText, size_t sourceTextSize, const HSQOBJECT *bindings, const char *sourcename, SQObjectPtr &out, bool raiseerror) { Arena astArena(_ss(vm)->_alloc_ctx, "AST"); RootBlock *r = parseASTImpl(&astArena, vm, sourceText, sourceTextSize, sourcename, nullptr, raiseerror); if (!r) return false; Arena cgArena(_ss(vm)->_alloc_ctx, "Codegen"); SQCompilationContext ctx(vm, &cgArena, sourcename, sourceText, sourceTextSize, nullptr, raiseerror); CodeGenVisitor codegen(&cgArena, bindings, vm, sourcename, ctx); return codegen.generate(r, out); } static bool TranslateASTToBytecodeImpl(SQVM *vm, RootBlock *ast, const HSQOBJECT *bindings, const char *sourcename, const char *sourceText, size_t sourceTextSize, SQObjectPtr &out, const Comments *comments, bool raiseerror) { Arena cgArena(_ss(vm)->_alloc_ctx, "Codegen"); SQCompilationContext ctx(vm, &cgArena, sourcename, sourceText, sourceTextSize, comments, raiseerror); CodeGenVisitor codegen(&cgArena, bindings, vm, sourcename, ctx); return codegen.generate(ast, out); } bool TranslateASTToBytecode(SQVM *vm, SqASTData *astData, const HSQOBJECT *bindings, const char *sourceText, size_t sourceTextSize, SQObjectPtr &out, bool raiseerror) { return TranslateASTToBytecodeImpl(vm, astData->root, bindings, astData->sourceName, sourceText, sourceTextSize, out, astData->comments, raiseerror); } void AnalyzeCode(SQVM *vm, SqASTData *astData, const HSQOBJECT *bindings, const char *sourceText, size_t sourceTextSize) { Arena saArena(_ss(vm)->_alloc_ctx, "Analyzer"); SQCompilationContext ctx(vm, &saArena, astData->sourceName, sourceText, sourceTextSize, astData->comments, true); RootBlock *ast = astData->root; StaticAnalyzer sa(ctx); sa.runAnalysis(ast, bindings); } bool AstGetImports(HSQUIRRELVM v, SQCompilation::SqASTData *astData, SQInteger *num, SQModuleImport **imports) { if (!astData || !astData->root || !num || !imports) return false; const ArenaVector &stmts = astData->root->statements(); size_t importCount = 0; for (size_t i = 0; i < stmts.size(); i++) { TreeOp op = stmts[i]->op(); if (op == TO_IMPORT) importCount++; else if (op != TO_DIRECTIVE) break; } if (importCount == 0) { *num = 0; *imports = nullptr; return true; } SQModuleImport *importArrayOut = (SQModuleImport *)sq_vm_malloc(_ss(v)->_alloc_ctx, sizeof(SQModuleImport) * importCount); memset(importArrayOut, 0, sizeof(SQModuleImport) * importCount); size_t importIdx = 0; for (size_t i = 0; i < stmts.size(); i++) { if (stmts[i]->op() == TO_DIRECTIVE) continue; if (stmts[i]->op() != TO_IMPORT) break; ImportStmt *importStmt = static_cast(stmts[i]); size_t numSlots = importStmt->slots.size(); SQModuleImport &imp = importArrayOut[importIdx]; imp.name = importStmt->moduleName; imp.alias = importStmt->moduleAlias; imp.numSlots = (int)numSlots; imp.slots = numSlots ? importStmt->slots.begin() : nullptr; imp.line = importStmt->lineStart(); imp.nameColumn = importStmt->nameCol; imp.aliasColumn = importStmt->aliasCol; importIdx++; } *num = (SQInteger)importCount; *imports = importArrayOut; return true; } void AstFreeImports(HSQUIRRELVM v, SQInteger num, SQModuleImport *imports) { if (!imports || num <= 0) return; sq_vm_free(_ss(v)->_alloc_ctx, imports, sizeof(SQModuleImport) * num); } }; // SQCompilation #endif ================================================ FILE: squirrel/compiler/compiler.h ================================================ /* see copyright notice in squirrel.h */ #pragma once struct SQVM; class OutputStream; class Arena; namespace SQCompilation { class RootBlock; class Comments; struct SqASTData { Arena *astArena; RootBlock *root; char sourceName[256]; Comments *comments; }; typedef void(*CompilerErrorFunc)(void *ud, const char *s); bool Compile(SQVM *vm, const char *sourceText, size_t sourceTextSize, const HSQOBJECT *bindings, const char *sourcename, SQObjectPtr &out, bool raiseerror); SqASTData *ParseToAST(SQVM *vm, const char *sourceText, size_t sourceTextSize, const char *sourcename, bool preserveComments, bool raiseerror); bool TranslateASTToBytecode(SQVM *vm, SqASTData *astData, const HSQOBJECT *bindings, const char *sourceText, size_t sourceTextSize, SQObjectPtr &out, bool raiseerror); void AnalyzeCode(SQVM *vm, SqASTData *astData, const HSQOBJECT *bindings, const char *sourceText, size_t sourceTextSize); bool AstGetImports(HSQUIRRELVM v, SQCompilation::SqASTData *astData, SQInteger *num, SQModuleImport **imports); void AstFreeImports(HSQUIRRELVM v, SQInteger num, SQModuleImport *imports); }; // SQCompilation ================================================ FILE: squirrel/compiler/constgen.cpp ================================================ #include "sqpcheader.h" #ifndef NO_COMPILER #include #include "opcodes.h" #include "sqstring.h" #include "sqfuncproto.h" #include "sqfuncstate.h" #include "codegen.h" #include "constgen.h" #include "sqvm.h" #include "optimizer.h" #include "sqtable.h" #include "sqarray.h" #include "sqclass.h" namespace SQCompilation { void ConstGenVisitor::throwUnsupported(Node *n, const char *type) { _ctx.reportDiagnostic(DiagnosticsId::DI_NOT_ALLOWED_IN_CONST, n->lineStart(), n->columnStart(), n->textWidth(), type); } void ConstGenVisitor::throwGeneralError(Node *n, const char *msg) { _ctx.reportDiagnostic(DiagnosticsId::DI_GENERAL_COMPILE_ERROR, n->lineStart(), n->columnStart(), n->textWidth(), msg); } void ConstGenVisitor::throwGeneralErrorFmt(Node *n, const char *fmt, ...) { char buf[256]; va_list args; va_start(args, fmt); vsnprintf(buf, sizeof(buf), fmt, args); va_end(args); _ctx.reportDiagnostic(DiagnosticsId::DI_GENERAL_COMPILE_ERROR, n->lineStart(), n->columnStart(), n->textWidth(), buf); } void ConstGenVisitor::process(Expr *expr, SQObjectPtr &out) { assert(sq_type(_result) == OT_NULL); expr->visit(this); out = _result; } SQObjectPtr ConstGenVisitor::convertLiteral(LiteralExpr *lit) { SQObjectPtr ret{}; switch (lit->kind()) { case LK_STRING: return _fs->CreateString(lit->s()); case LK_FLOAT: ret._type = OT_FLOAT; ret._unVal.fFloat = lit->f(); break; case LK_INT: ret._type = OT_INTEGER; ret._unVal.nInteger = lit->i(); break; case LK_BOOL: ret._type = OT_BOOL; ret._unVal.nInteger = lit->b() ? 1 : 0; break; case LK_NULL: ret._type = OT_NULL; ret._unVal.raw = 0; break; } return ret; } void ConstGenVisitor::visitLiteralExpr(LiteralExpr *expr) { _result = convertLiteral(expr); _call_target.Null(); } void ConstGenVisitor::visitArrayExpr(ArrayExpr *expr) { SQArray *arr = SQArray::Create(_ss(_vm), expr->initializers().size()); for (SQUnsignedInteger i = 0; i < expr->initializers().size(); ++i) { Expr *valExpr = expr->initializers()[i]; valExpr->visit(this); arr->Set(i, _result); } _result = SQObjectPtr(arr); _result._flags = SQOBJ_FLAG_IMMUTABLE; _call_target.Null(); } void ConstGenVisitor::visitTableExpr(TableExpr *tblExpr) { SQTable *table = SQTable::Create(_ss(_vm), tblExpr->members().size()); const auto &members = tblExpr->members(); for (SQUnsignedInteger i = 0; i < members.size(); ++i) { const TableMember &m = members[i]; m.key->visit(this); SQObjectPtr key(_result); m.value->visit(this); SQObjectPtr value(_result); table->NewSlot(key, value); } _result = SQObjectPtr(table); _result._flags = SQOBJ_FLAG_IMMUTABLE; _call_target.Null(); } void ConstGenVisitor::visitId(Id *id) { SQObjectPtr idObj = _fs->CreateString(id->name()); SQObjectPtr constant; if (_codegen.IsConstant(idObj, constant)) { _result = constant; } else { _ctx.reportDiagnostic(DiagnosticsId::DI_ID_IS_NOT_CONST, id->lineStart(), id->columnStart(), id->textWidth(), _stringval(idObj)); } _call_target.Null(); } void ConstGenVisitor::visitCallExpr(CallExpr *expr) { if (expr->isNullable()) { throwGeneralError(expr, "Nullable calls are not supported in constant expressions"); return; } ConstGenVisitor funcEval(_vm, _fs, _ctx, _codegen); expr->callee()->visit(&funcEval); SQObjectPtr func = funcEval._result; if (!sq_is_pure_function(&func)) throwGeneralError(expr, "Only calls to pure functions are allowed in constant expressions"); sqvector argValues(nullptr); argValues.reserve(expr->arguments().size()); auto &argExprs = expr->arguments(); for (SQUnsignedInteger i = 0; i < argExprs.size(); ++i) { ConstGenVisitor argEval(_vm, _fs, _ctx, _codegen); argExprs[i]->visit(&argEval); argValues.push_back(argEval._result); } SQInteger prevTop = sq_gettop(_vm); sq_pushobject(_vm, func); sq_pushobject(_vm, funcEval._call_target); for (SQUnsignedInteger i = 0; i < argValues.size(); ++i) { sq_pushobject(_vm, argValues[i]); } if (SQ_FAILED(sq_call(_vm, 1 + argValues.size(), SQTrue, SQTrue))) { sq_settop(_vm, prevTop); throwGeneralError(expr, "Failed to evaluate constant initializer call"); } HSQOBJECT callRes; sq_getstackobj(_vm, -1, &callRes); callRes._flags = SQOBJ_FLAG_IMMUTABLE; _result = SQObjectPtr(callRes); _call_target.Null(); sq_settop(_vm, prevTop); } void ConstGenVisitor::visitGetFieldExpr(GetFieldExpr *expr) { expr->visitChildren(this); SQObjectPtr container(_result); _call_target = container; SQObjectPtr slotName = _fs->CreateString(expr->fieldName()); SQInteger prevTop = sq_gettop(_vm); sq_pushobject(_vm, container); sq_pushobject(_vm, slotName); bool hasSlot = SQ_SUCCEEDED(sq_get(_vm, -2)); if (hasSlot) { HSQOBJECT value; sq_getstackobj(_vm, -1, &value); value._flags = SQOBJ_FLAG_IMMUTABLE; _result = SQObjectPtr(value); } else if (expr->isNullable()) { if (!sq_isnull(_vm->_lasterror)) { SQObjectPtr err = _vm->_lasterror; sq_reseterror(_vm); sq_settop(_vm, prevTop); throwGeneralErrorFmt(expr, "error in get operation: %s", sq_isstring(err) ? _stringval(err) : ""); } _result.Null(); } else { sq_settop(_vm, prevTop); _ctx.reportDiagnostic(DiagnosticsId::DI_CONSTANT_FIELD_NOT_FOUND, expr->lineStart(), expr->columnStart(), expr->textWidth(), _stringval(slotName)); } sq_settop(_vm, prevTop); } void ConstGenVisitor::visitGetSlotExpr(GetSlotExpr *expr) { expr->receiver()->visit(this); SQObjectPtr container(_result); _call_target = container; expr->key()->visit(this); SQObjectPtr key(_result); SQInteger prevTop = sq_gettop(_vm); sq_pushobject(_vm, container); sq_pushobject(_vm, key); bool hasSlot = SQ_SUCCEEDED(sq_get(_vm, -2)); if (hasSlot) { HSQOBJECT value; sq_getstackobj(_vm, -1, &value); value._flags = SQOBJ_FLAG_IMMUTABLE; _result = SQObjectPtr(value); } else if (expr->isNullable()) { if (!sq_isnull(_vm->_lasterror)) { SQObjectPtr err = _vm->_lasterror; sq_reseterror(_vm); sq_settop(_vm, prevTop); throwGeneralErrorFmt(expr, "error in get operation: %s", sq_isstring(err) ? _stringval(err) : ""); } _result.Null(); } else { SQObjectPtr keyAsString; sq_pushobject(_vm, key); if (SQ_SUCCEEDED(sq_tostring(_vm, -1))) { SQObject t; sq_getstackobj(_vm, -1, &t); keyAsString = t; } else { keyAsString = _fs->CreateString("", 5); } sq_settop(_vm, prevTop); Expr *errNode = expr->key(); _ctx.reportDiagnostic(DiagnosticsId::DI_CONSTANT_SLOT_NOT_FOUND, errNode->lineStart(), errNode->columnStart(), errNode->textWidth(), IdType2Name(sq_type(key)), _stringval(keyAsString)); } sq_settop(_vm, prevTop); } void ConstGenVisitor::visitUnExpr(UnExpr *unary) { sq_reseterror(_vm); _call_target.Null(); switch (unary->op()) { case TO_NEG: unary->argument()->visit(this); if (!_vm->NEG_OP(_result, _result)) { SQObjectPtr err = _vm->_lasterror; sq_reseterror(_vm); throwGeneralError(unary->argument(), sq_isstring(err) ? _stringval(err) : "negation failed"); } break; case TO_NOT: unary->argument()->visit(this); _result = SQObjectPtr(_vm->IsFalse(_result)); break; case TO_BNOT: unary->argument()->visit(this); if(sq_type(_result) != OT_INTEGER) throwGeneralError(unary->argument(), "attempt to perform a bitwise op on a non-integer"); _result = SQObjectPtr(~(_integer(_result))); break; case TO_TYPEOF: unary->argument()->visit(this); if (!_vm->TypeOf(_result, _result)) throwGeneralError(unary->argument(), "typeof call failed"); break; case TO_INLINE_CONST: case TO_PAREN: // bypass unary->argument()->visit(this); break; default: throwUnsupported(unary, "this unary expression"); } } void ConstGenVisitor::visitBinExpr(BinExpr *expr) { sq_reseterror(_vm); _call_target.Null(); switch (expr->op()) { case TO_NEWSLOT: throwUnsupported(expr, "new slot expression"); case TO_ASSIGN: throwUnsupported(expr, "assignment expression"); case TO_PLUSEQ: case TO_MINUSEQ: case TO_MULEQ: case TO_DIVEQ: case TO_MODEQ: throwUnsupported(expr, "compound arithmetic expression"); default: break; } expr->lhs()->visit(this); SQObjectPtr lhs(_result); expr->rhs()->visit(this); SQObjectPtr rhs(_result); assert(sq_isnull(_vm->_lasterror) && "Error thrown while evaluating binary expression operands"); bool ok = true; switch (expr->op()) { case TO_NULLC: _result = sq_isnull(lhs) ? rhs : lhs; break; case TO_OROR: _result = SQVM::IsFalse(lhs) ? rhs : lhs; break; case TO_ANDAND: _result = SQVM::IsFalse(lhs) ? lhs : rhs; break; case TO_ADD: ok = _vm->ARITH_OP('+', _result, lhs, rhs); break; case TO_SUB: ok = _vm->ARITH_OP('-', _result, lhs, rhs); break; case TO_MUL: ok = _vm->ARITH_OP('*', _result, lhs, rhs); break; case TO_DIV: ok = _vm->ARITH_OP('/', _result, lhs, rhs); break; case TO_MOD: ok = _vm->ARITH_OP('%', _result, lhs, rhs); break; case TO_OR: ok = _vm->BW_OP(BW_OR, _result, lhs, rhs); break; case TO_AND: ok = _vm->BW_OP(BW_AND, _result, lhs, rhs); break; case TO_XOR: ok = _vm->BW_OP(BW_XOR, _result, lhs, rhs); break; case TO_USHR: ok = _vm->BW_OP(BW_USHIFTR, _result, lhs, rhs); break; case TO_SHR: ok = _vm->BW_OP(BW_SHIFTR, _result, lhs, rhs); break; case TO_SHL: ok = _vm->BW_OP(BW_SHIFTL, _result, lhs, rhs); break; case TO_EQ: _result = SQObjectPtr(_vm->IsEqual(lhs, rhs)); break; case TO_NE: _result = SQObjectPtr(!_vm->IsEqual(lhs, rhs)); break; case TO_GE: ok = _vm->CMP_OP(CMP_GE, lhs, rhs, _result); break; case TO_GT: ok = _vm->CMP_OP(CMP_G, lhs, rhs, _result); break; case TO_LE: ok = _vm->CMP_OP(CMP_LE, lhs, rhs, _result); break; case TO_LT: ok = _vm->CMP_OP(CMP_L, lhs, rhs, _result); break; case TO_3CMP: ok = _vm->CMP_OP(CMP_3W, lhs, rhs, _result); break; case TO_IN: { SQObjectPtr tmpVal; bool exists = _vm->Get(rhs, lhs, tmpVal, GET_FLAG_DO_NOT_RAISE_ERROR | GET_FLAG_NO_TYPE_METHODS); _result = SQObjectPtr(exists); if (!sq_isnull(_vm->_lasterror)) { // handle errors that can happen in _get() metamethod ok = false; _vm->Raise_Error("Error while applying 'in' operator: %s", sq_isstring(_vm->_lasterror) ? _stringval(_vm->_lasterror) : ""); } break; } case TO_INSTANCEOF: { if (sq_type(rhs) != OT_CLASS) throwGeneralError(expr->rhs(), "checking instance with non-class"); _result = SQObjectPtr(SQVM::IsInstanceOf(lhs, _class(rhs))); break; } default: assert(!"Unknown binary expression"); break; } assert(ok == sq_isnull(_vm->_lasterror)); if (!ok) { SQObjectPtr err = _vm->_lasterror; sq_reseterror(_vm); throwGeneralError(expr, sq_isstring(err) ? _stringval(err) : "internal error in binary operation"); } } void ConstGenVisitor::visitTerExpr(TerExpr *expr) { _call_target.Null(); expr->a()->visit(this); Expr *resBranch = SQVM::IsFalse(_result) ? expr->c() : expr->b(); resBranch->visit(this); } void ConstGenVisitor::visitFunctionExpr(FunctionExpr *funcExpr) { _call_target.Null(); _result = _codegen.compileConstFunc(funcExpr); } } #endif ================================================ FILE: squirrel/compiler/constgen.h ================================================ #pragma once #include "ast.h" #include "opcodes.h" #include "compilationcontext.h" #include struct SQFuncState; namespace SQCompilation { class ConstGenVisitor : public Visitor { private: SQCompilationContext &_ctx; SQVM *_vm; SQFuncState *_fs; CodeGenVisitor &_codegen; SQObjectPtr _result; SQObjectPtr _call_target; //< must null it in every node visit and assign the container in get operations public: ConstGenVisitor(SQVM *vm, SQFuncState *fs, SQCompilationContext &ctx, CodeGenVisitor &codegen) : _ctx(ctx), _fs(fs), _vm(vm), _codegen(codegen) { } public: virtual ~ConstGenVisitor() {} void process(Expr *expr, SQObjectPtr &out); virtual void visitNode(Node *node) { node->visitChildren(this); } virtual void visitExpr(Expr *expr) { visitNode(expr); } virtual void visitUnExpr(UnExpr *expr); virtual void visitBinExpr(BinExpr *expr); virtual void visitTerExpr(TerExpr *expr); virtual void visitCallExpr(CallExpr *expr); virtual void visitId(Id *id); virtual void visitAccessExpr(AccessExpr *expr) { throwUnsupported(expr, "access expression"); } virtual void visitGetFieldExpr(GetFieldExpr *expr); virtual void visitSetFieldExpr(SetFieldExpr *expr) { throwUnsupported(expr, "set field expression"); } virtual void visitGetSlotExpr(GetSlotExpr *expr); virtual void visitSetSlotExpr(SetSlotExpr *expr) { throwUnsupported(expr, "set slot expression"); } virtual void visitBaseExpr(BaseExpr *expr) { throwUnsupported(expr, "base expression"); } virtual void visitRootTableAccessExpr(RootTableAccessExpr *expr) { throwUnsupported(expr, "root table access expression"); } virtual void visitLiteralExpr(LiteralExpr *expr); virtual void visitIncExpr(IncExpr *expr) { throwUnsupported(expr, "increment expression"); } virtual void visitArrayExpr(ArrayExpr *expr); virtual void visitTableExpr(TableExpr *tbl); virtual void visitClassExpr(ClassExpr *cls) { visitTableExpr(cls); } virtual void visitFunctionExpr(FunctionExpr *f); virtual void visitCommaExpr(CommaExpr *expr) { throwUnsupported(expr, "comma expression"); } virtual void visitExternalValueExpr(ExternalValueExpr *expr) { throwUnsupported(expr, "external value expression"); } virtual void visitStmt(Statement *stmt) { throwUnsupported(stmt, "statement"); } virtual void visitBlock(Block *block) { throwUnsupported(block, "block statement"); } virtual void visitIfStatement(IfStatement *ifstmt) { throwUnsupported(ifstmt, "if statement"); } virtual void visitLoopStatement(LoopStatement *loop) { throwUnsupported(loop, "loop statement"); } virtual void visitWhileStatement(WhileStatement *loop) { throwUnsupported(loop, "while statement"); } virtual void visitDoWhileStatement(DoWhileStatement *loop) { throwUnsupported(loop, "do-while statement"); } virtual void visitForStatement(ForStatement *loop) { throwUnsupported(loop, "for statement"); } virtual void visitForeachStatement(ForeachStatement *loop) { throwUnsupported(loop, "foreach statement"); } virtual void visitSwitchStatement(SwitchStatement *swtch) { throwUnsupported(swtch, "switch statement"); } virtual void visitTryStatement(TryStatement *tr) { throwUnsupported(tr, "try statement"); } virtual void visitTerminateStatement(TerminateStatement *term) { throwUnsupported(term, "terminate statement"); } virtual void visitReturnStatement(ReturnStatement *ret) { throwUnsupported(ret, "return statement"); } virtual void visitYieldStatement(YieldStatement *yld) { throwUnsupported(yld, "yield statement"); } virtual void visitThrowStatement(ThrowStatement *thr) { throwUnsupported(thr, "throw statement"); } virtual void visitJumpStatement(JumpStatement *jmp) { throwUnsupported(jmp, "jump statement"); } virtual void visitBreakStatement(BreakStatement *jmp) { throwUnsupported(jmp, "break statement"); } virtual void visitContinueStatement(ContinueStatement *jmp) { throwUnsupported(jmp, "continue statement"); } virtual void visitExprStatement(ExprStatement *estmt) { throwUnsupported(estmt, "expression statement"); } virtual void visitEmptyStatement(EmptyStatement *empty) { throwUnsupported(empty, "empty statement"); } virtual void visitDecl(Decl *decl) { throwUnsupported(decl, "declaration"); } virtual void visitValueDecl(ValueDecl *decl) { throwUnsupported(decl, "value declaration"); } virtual void visitVarDecl(VarDecl *decl) { throwUnsupported(decl, "variable declaration"); } virtual void visitParamDecl(ParamDecl *decl) { throwUnsupported(decl, "parameter declaration"); } virtual void visitConstDecl(ConstDecl *cnst) { visitDecl(cnst); } virtual void visitEnumDecl(EnumDecl *enm) { visitDecl(enm); } virtual void visitDeclGroup(DeclGroup *grp) { visitDecl(grp); } virtual void visitDestructuringDecl(DestructuringDecl *destruct) { visitDecl(destruct); } virtual void visitDirectiveStatement(DirectiveStmt *dir) { throwUnsupported(dir, "directive statement"); } private: void throwUnsupported(Node *n, const char *type); void throwGeneralError(Node *n, const char *msg); void throwGeneralErrorFmt(Node *n, const char *fmt, ...); SQObjectPtr convertLiteral(LiteralExpr *lit); }; } // namespace SQCompilation ================================================ FILE: squirrel/compiler/lex_tokens.h ================================================ #pragma once #define TK_IDENTIFIER 258 #define TK_STRING_LITERAL 259 #define TK_INTEGER 260 #define TK_FLOAT 261 #define TK_BASE 262 #define TK_DELETE 263 #define TK_EQ 264 #define TK_NE 265 #define TK_LE 266 #define TK_GE 267 #define TK_SWITCH 268 #define TK_ARROW 269 #define TK_AND 270 #define TK_OR 271 #define TK_IF 272 #define TK_ELSE 273 #define TK_WHILE 274 #define TK_BREAK 275 #define TK_FOR 276 #define TK_DO 277 #define TK_NULL 278 #define TK_FOREACH 279 #define TK_IN 280 #define TK_NEWSLOT 281 #define TK_MODULO 282 #define TK_LOCAL 283 #define TK_CLONE 284 #define TK_FUNCTION 285 #define TK_RETURN 286 #define TK_TYPEOF 287 #define TK_UMINUS 288 #define TK_PLUSEQ 289 #define TK_MINUSEQ 290 #define TK_CONTINUE 291 #define TK_YIELD 292 #define TK_TRY 293 #define TK_CATCH 294 #define TK_THROW 295 #define TK_SHIFTL 296 #define TK_SHIFTR 297 #define TK_RESUME 298 #define TK_DOUBLE_COLON 299 #define TK_CASE 300 #define TK_DEFAULT 301 #define TK_THIS 302 #define TK_PLUSPLUS 303 #define TK_MINUSMINUS 304 #define TK_3WAYSCMP 305 #define TK_USHIFTR 306 #define TK_CLASS 307 #define TK_EXTENDS 308 #define TK_CONSTRUCTOR 310 #define TK_INSTANCEOF 311 #define TK_VARPARAMS 312 #define TK___LINE__ 313 #define TK___FILE__ 314 #define TK_TRUE 315 #define TK_FALSE 316 #define TK_MULEQ 317 #define TK_DIVEQ 318 #define TK_MODEQ 319 //#define TK_ 320 //#define TK_ 321 #define TK_STATIC 322 #define TK_ENUM 323 #define TK_CONST 324 #define TK_RESERVED_001 325 #define TK_NULLGETSTR 326 #define TK_NULLGETOBJ 327 #define TK_NULLCOALESCE 328 #define TK_NULLCALL 329 //#define TK_ 330 #define TK_GLOBAL 331 #define TK_DIRECTIVE 332 #define TK_READERMACRO 333 #define TK_NOT 334 //#define TK_ 335 #define TK_LET 336 #define TK_TEMPLATE_PREFIX 337 #define TK_TEMPLATE_INFIX 338 #define TK_TEMPLATE_SUFFIX 339 #define TK_TEMPLATE_OP 340 #define TK_TYPE_METHOD_GETSTR 341 #define TK_NULLABLE_TYPE_METHOD_GETSTR 342 #define TK_DOCSTRING 343 #define TK_CODE_BLOCK_EXPR 344 ================================================ FILE: squirrel/compiler/lexer.cpp ================================================ /* see copyright notice in squirrel.h */ #include "sqpcheader.h" #include #include #include "sqtable.h" #include "sqstring.h" #include "lexer.h" #include "lex_tokens.h" #include #define CUR_CHAR (_currdata) #define RETURN_TOKEN(t) { _prevtoken = _curtoken; _prevflags = _flags; _flags = 0; _curtoken = t; return t;} #define IS_EOB() (CUR_CHAR <= SQUIRREL_EOB) #define NEXT() {Next();_currentcolumn++;} #define INIT_TEMP_STRING() { _longstr.resize(0);} #define APPEND_CHAR(c) { _longstr.push_back(c);} #define TERMINATE_BUFFER() {_longstr.push_back('\0');} #define ADD_KEYWORD(key,id) _keywords->NewSlot( SQObjectPtr(SQString::Create(_sharedstate, #key)), SQObjectPtr(SQInteger(id))) using namespace SQCompilation; SQLexer::SQLexer(SQSharedState *ss, SQCompilationContext &ctx, Comments *comments) : _sharedstate(ss) , _longstr(ss->_alloc_ctx) , _ctx(ctx) , _comments(comments) , _currentComment(ss->_alloc_ctx) { } SQLexer::~SQLexer() { _keywords->Release(); } using CommentVec = sqvector; void SQLexer::Init(const char *sourceText, size_t sourceTextSize) { _keywords = SQTable::Create(_sharedstate, 39); ADD_KEYWORD(while, TK_WHILE); ADD_KEYWORD(do, TK_DO); ADD_KEYWORD(if, TK_IF); ADD_KEYWORD(else, TK_ELSE); ADD_KEYWORD(break, TK_BREAK); ADD_KEYWORD(continue, TK_CONTINUE); ADD_KEYWORD(return, TK_RETURN); ADD_KEYWORD(null, TK_NULL); ADD_KEYWORD(function, TK_FUNCTION); ADD_KEYWORD(local, TK_LOCAL); ADD_KEYWORD(for, TK_FOR); ADD_KEYWORD(foreach, TK_FOREACH); ADD_KEYWORD(in, TK_IN); ADD_KEYWORD(typeof, TK_TYPEOF); ADD_KEYWORD(base, TK_BASE); ADD_KEYWORD(delete, TK_DELETE); ADD_KEYWORD(try, TK_TRY); ADD_KEYWORD(catch, TK_CATCH); ADD_KEYWORD(throw, TK_THROW); ADD_KEYWORD(clone, TK_CLONE); ADD_KEYWORD(yield, TK_YIELD); ADD_KEYWORD(resume, TK_RESUME); ADD_KEYWORD(switch, TK_SWITCH); ADD_KEYWORD(case, TK_CASE); ADD_KEYWORD(default, TK_DEFAULT); ADD_KEYWORD(this, TK_THIS); ADD_KEYWORD(class,TK_CLASS); ADD_KEYWORD(constructor,TK_CONSTRUCTOR); ADD_KEYWORD(instanceof,TK_INSTANCEOF); ADD_KEYWORD(true,TK_TRUE); ADD_KEYWORD(false,TK_FALSE); ADD_KEYWORD(static,TK_STATIC); ADD_KEYWORD(enum,TK_ENUM); ADD_KEYWORD(const,TK_CONST); ADD_KEYWORD(__LINE__,TK___LINE__); ADD_KEYWORD(__FILE__,TK___FILE__); ADD_KEYWORD(global, TK_GLOBAL); ADD_KEYWORD(not, TK_NOT); ADD_KEYWORD(let, TK_LET); _sourceText = sourceText; _sourceTextSize = sourceTextSize; _sourceTextPtr = 0; _lasttokenline = _currentline = 1; _lasttokencolumn = _currentcolumn = 0; _prevtoken = -1; _tokencolumn = 0; _tokenline = 1; _reached_eof = SQFalse; if (_comments) _comments->pushNewLine(); Next(); } void SQLexer::Next() { if (_sourceTextPtr >= _sourceTextSize) { _reached_eof = SQTrue; _currdata = SQUIRREL_EOB; } else { _currdata = _sourceText[_sourceTextPtr++]; } } const char *SQLexer::Tok2Str(SQInteger tok) { SQObjectPtr itr, key, val; SQInteger nitr; while((nitr = _keywords->Next(false,itr, key, val)) != -1) { itr = (SQInteger)nitr; if(((SQInteger)_integer(val)) == tok) return _stringval(key); } return NULL; } void SQLexer::SetStringValue() { _svalue = &_longstr[0]; } void SQLexer::AddComment(enum CommentKind kind, SQInteger line, SQInteger start, SQInteger end) { if (!_comments) return; size_t size = _currentComment.size(); char *data = (char *)sq_vm_malloc(_sharedstate->_alloc_ctx, (size + 1) * sizeof(char)); memcpy(data, &_currentComment[0], size); data[size] = '\0'; CurLineComments().push_back({ kind, size, data, line, start, end }); _currentComment.clear(); } void SQLexer::LexBlockComment() { enum CommentKind k = CK_BLOCK; SQInteger line = 1; SQInteger start = _currentcolumn; bool done = false; while(!done) { _currentComment.push_back(CUR_CHAR); switch(CUR_CHAR) { case '*': { NEXT(); if(CUR_CHAR == '/') { done = true; NEXT(); }}; continue; case '\n': k = CK_ML_BLOCK; AddComment(k, line, start, _currentcolumn); ++line; nextLine(); NEXT(); start = _currentcolumn; continue; case SQUIRREL_EOB: _ctx.reportDiagnostic(DiagnosticsId::DI_TRAILING_BLOCK_COMMENT, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn); return; default: NEXT(); } } AddComment(k, k == CK_ML_BLOCK ? line : 0, start, _currentcolumn); } void SQLexer::LexLineComment() { SQInteger start = _currentcolumn; do { NEXT(); _currentComment.push_back(CUR_CHAR); } while (CUR_CHAR != '\n' && (!IS_EOB())); AddComment(CK_LINE, 0, start, _currentcolumn); } SQInteger SQLexer::Lex() { return LexSingleToken(); } void SQLexer::nextLine() { ++_currentline; if (_comments) _comments->pushNewLine(); } SQInteger SQLexer::LexSingleToken() { _lasttokenline = _currentline; _lasttokencolumn = _currentcolumn; if (_state == LS_TEMPLATE && _expectedToken != TK_TEMPLATE_PREFIX) { SQInteger stype = ReadString('"', false, false); if (stype != -1) RETURN_TOKEN(stype); _ctx.reportDiagnostic(DiagnosticsId::DI_LEX_ERROR_PARSE, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn, "the string"); } while(CUR_CHAR != SQUIRREL_EOB) { _tokenline = _currentline; _tokencolumn = _currentcolumn; switch(CUR_CHAR){ case '\t': case '\r': case ' ': _flags |= TF_PREP_SPACE; NEXT(); continue; case '\n': nextLine(); _prevtoken=_curtoken; _flags |= TF_PREP_EOL; _curtoken='\n'; NEXT(); _currentcolumn=0; continue; case '/': NEXT(); switch(CUR_CHAR){ case '*': NEXT(); LexBlockComment(); continue; case '/': LexLineComment(); continue; case '=': NEXT(); RETURN_TOKEN(TK_DIVEQ); continue; default: RETURN_TOKEN('/'); } case '=': NEXT(); if (CUR_CHAR != '='){ RETURN_TOKEN('=') } else { NEXT(); RETURN_TOKEN(TK_EQ); } case '<': NEXT(); switch(CUR_CHAR) { case '=': NEXT(); if(CUR_CHAR == '>') { NEXT(); RETURN_TOKEN(TK_3WAYSCMP); } RETURN_TOKEN(TK_LE) break; case '-': NEXT(); RETURN_TOKEN(TK_NEWSLOT); break; case '<': NEXT(); RETURN_TOKEN(TK_SHIFTL); break; } RETURN_TOKEN('<'); case '>': NEXT(); if (CUR_CHAR == '='){ NEXT(); RETURN_TOKEN(TK_GE);} else if(CUR_CHAR == '>'){ NEXT(); if(CUR_CHAR == '>'){ NEXT(); RETURN_TOKEN(TK_USHIFTR); } RETURN_TOKEN(TK_SHIFTR); } else { RETURN_TOKEN('>') } case '!': NEXT(); if (CUR_CHAR != '='){ RETURN_TOKEN('!')} else { NEXT(); RETURN_TOKEN(TK_NE); } case '#': { NEXT(); SQInteger stype = ReadDirective(); if (stype < 0) _ctx.reportDiagnostic(DiagnosticsId::DI_LEX_ERROR_PARSE, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn, "directive"); RETURN_TOKEN(TK_DIRECTIVE); } case '$': { NEXT(); if (CUR_CHAR == '"') { RETURN_TOKEN(TK_TEMPLATE_OP); } else if (CUR_CHAR == '$') { // $$ NEXT(); if (CUR_CHAR == '{') { // $${ NEXT(); RETURN_TOKEN(TK_CODE_BLOCK_EXPR); } else RETURN_TOKEN('$'); } else { RETURN_TOKEN('$'); } } case '@': { SQInteger stype; NEXT(); if (CUR_CHAR == '@') { NEXT(); stype = ReadString('"', true); if (stype != TK_STRING_LITERAL) _ctx.reportDiagnostic(DiagnosticsId::DI_LEX_ERROR_PARSE, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn, "the docstring"); RETURN_TOKEN(TK_DOCSTRING); } if (CUR_CHAR != '"') { RETURN_TOKEN('@'); } if ((stype = ReadString('"', true)) != -1) { RETURN_TOKEN(stype); } _ctx.reportDiagnostic(DiagnosticsId::DI_LEX_ERROR_PARSE, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn, "the string"); break; } case '"': case '\'': { SQInteger stype; if((stype=ReadString(CUR_CHAR,false))!=-1){ RETURN_TOKEN(stype); } _ctx.reportDiagnostic(DiagnosticsId::DI_LEX_ERROR_PARSE, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn, "the string"); break; } case '{': case '}': case '(': case ')': case '[': case ']': case ';': case ',': case '^': case '~': {SQInteger ret = CUR_CHAR; NEXT(); RETURN_TOKEN(ret); } case '?': {NEXT(); if (CUR_CHAR == '.') { NEXT(); if (CUR_CHAR == '$') { NEXT(); RETURN_TOKEN(TK_NULLABLE_TYPE_METHOD_GETSTR); } else { RETURN_TOKEN(TK_NULLGETSTR); } } if (CUR_CHAR == '[') { NEXT(); RETURN_TOKEN(TK_NULLGETOBJ); } if (CUR_CHAR == '(') { NEXT(); RETURN_TOKEN(TK_NULLCALL); } if (CUR_CHAR == '?') { NEXT(); RETURN_TOKEN(TK_NULLCOALESCE); } RETURN_TOKEN('?'); } case '.': NEXT(); if (CUR_CHAR == '$') { NEXT(); RETURN_TOKEN(TK_TYPE_METHOD_GETSTR); } if (CUR_CHAR != '.'){ RETURN_TOKEN('.') } NEXT(); if (CUR_CHAR != '.'){ _ctx.reportDiagnostic(DiagnosticsId::DI_INVALID_TOKEN, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn, ".."); } NEXT(); RETURN_TOKEN(TK_VARPARAMS); case '&': NEXT(); if (CUR_CHAR != '&'){ RETURN_TOKEN('&') } else { NEXT(); RETURN_TOKEN(TK_AND); } case '|': NEXT(); if (CUR_CHAR != '|'){ RETURN_TOKEN('|') } else { NEXT(); RETURN_TOKEN(TK_OR); } case ':': NEXT(); if (CUR_CHAR != ':'){ RETURN_TOKEN(':') } else { NEXT(); RETURN_TOKEN(TK_DOUBLE_COLON); } case '*': NEXT(); if (CUR_CHAR == '='){ NEXT(); RETURN_TOKEN(TK_MULEQ);} else RETURN_TOKEN('*'); case '%': NEXT(); if (CUR_CHAR == '='){ NEXT(); RETURN_TOKEN(TK_MODEQ);} else RETURN_TOKEN('%'); case '-': NEXT(); if (CUR_CHAR == '='){ NEXT(); RETURN_TOKEN(TK_MINUSEQ);} else if (CUR_CHAR == '-'){ NEXT(); RETURN_TOKEN(TK_MINUSMINUS);} else RETURN_TOKEN('-'); case '+': NEXT(); if (CUR_CHAR == '='){ NEXT(); RETURN_TOKEN(TK_PLUSEQ);} else if (CUR_CHAR == '+'){ NEXT(); RETURN_TOKEN(TK_PLUSPLUS);} else RETURN_TOKEN('+'); case SQUIRREL_EOB: return 0; default:{ if (sq_isdigit(CUR_CHAR)) { SQInteger ret = ReadNumber(); RETURN_TOKEN(ret); } else if (sq_isalpha(CUR_CHAR) || CUR_CHAR == '_') { SQInteger t = ReadID(); RETURN_TOKEN(t); } else { SQInteger c = CUR_CHAR; if (iscntrl((int)c)) _ctx.reportDiagnostic(DiagnosticsId::DI_UNEXPECTED_CHAR, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn, "(control)"); NEXT(); RETURN_TOKEN(c); } } } } return 0; } SQInteger SQLexer::GetIDType(const char *s,SQInteger len) { SQObjectPtr t; if(_keywords->GetStr(s,len, t)) { return SQInteger(_integer(t)); } return TK_IDENTIFIER; } SQInteger SQLexer::AddUTF8(SQUnsignedInteger ch) { if (ch < 0x80) { APPEND_CHAR((char)ch); return 1; } if (ch < 0x800) { APPEND_CHAR((char)((ch >> 6) | 0xC0)); APPEND_CHAR((char)((ch & 0x3F) | 0x80)); return 2; } if (ch < 0x10000) { APPEND_CHAR((char)((ch >> 12) | 0xE0)); APPEND_CHAR((char)(((ch >> 6) & 0x3F) | 0x80)); APPEND_CHAR((char)((ch & 0x3F) | 0x80)); return 3; } if (ch < 0x110000) { APPEND_CHAR((char)((ch >> 18) | 0xF0)); APPEND_CHAR((char)(((ch >> 12) & 0x3F) | 0x80)); APPEND_CHAR((char)(((ch >> 6) & 0x3F) | 0x80)); APPEND_CHAR((char)((ch & 0x3F) | 0x80)); return 4; } return 0; } SQInteger SQLexer::ProcessStringHexEscape(char *dest, SQInteger maxdigits) { NEXT(); if (!sq_isxdigit(CUR_CHAR)) _ctx.reportDiagnostic(DiagnosticsId::DI_HEX_NUMBERS_EXPECTED, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn); SQInteger n = 0; while (sq_isxdigit(CUR_CHAR) && n < maxdigits) { dest[n] = CUR_CHAR; n++; NEXT(); } dest[n] = 0; return n; } SQInteger SQLexer::ReadString(SQInteger ndelim,bool verbatim, bool advance) { SQInteger t = TK_STRING_LITERAL; if (_state == LS_TEMPLATE && ndelim != '\"') { _ctx.reportDiagnostic(DiagnosticsId::DI_EXPECTED_LEX, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn, "string"); return -1; } INIT_TEMP_STRING(); if (advance) NEXT(); if(IS_EOB()) return -1; for(;;) { while(CUR_CHAR != ndelim) { SQInteger x = CUR_CHAR; switch (x) { case SQUIRREL_EOB: _ctx.reportDiagnostic(DiagnosticsId::DI_UNFINISHED_STRING, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn); return -1; case '\n': if(!verbatim) _ctx.reportDiagnostic(DiagnosticsId::DI_NEWLINE_IN_CONST, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn); APPEND_CHAR(CUR_CHAR); NEXT(); nextLine(); break; case '\\': if(verbatim) { APPEND_CHAR('\\'); NEXT(); } else { NEXT(); switch(CUR_CHAR) { case 'x': { const SQInteger maxdigits = sizeof(char) * 2; char temp[maxdigits + 1]; ProcessStringHexEscape(temp, maxdigits); char *stemp; APPEND_CHAR((char)strtoul(temp, &stemp, 16)); } break; case 'U': case 'u': { const SQInteger maxdigits = CUR_CHAR == 'u' ? 4 : 8; char temp[8 + 1]; ProcessStringHexEscape(temp, maxdigits); char *stemp; AddUTF8(strtoul(temp, &stemp, 16)); } break; case 't': APPEND_CHAR('\t'); NEXT(); break; case 'a': APPEND_CHAR('\a'); NEXT(); break; case 'b': APPEND_CHAR('\b'); NEXT(); break; case 'n': APPEND_CHAR('\n'); NEXT(); break; case 'r': APPEND_CHAR('\r'); NEXT(); break; case 'v': APPEND_CHAR('\v'); NEXT(); break; case 'f': APPEND_CHAR('\f'); NEXT(); break; case '0': APPEND_CHAR('\0'); NEXT(); break; case '\\': APPEND_CHAR('\\'); NEXT(); break; case '"': APPEND_CHAR('"'); NEXT(); break; case '\'': APPEND_CHAR('\''); NEXT(); break; case '{': case '}': if (_state == LS_TEMPLATE) { APPEND_CHAR(CUR_CHAR); NEXT(); break; } // fall through -V796 default: _ctx.reportDiagnostic(DiagnosticsId::DI_UNRECOGNISED_ESCAPER, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn); break; } } break; case '{': if (_state == LS_TEMPLATE) { APPEND_CHAR(CUR_CHAR); NEXT(); assert(_expectedToken > 0); t = _expectedToken; goto loop_exit; } default: APPEND_CHAR(CUR_CHAR); NEXT(); } } NEXT(); if (_state == LS_TEMPLATE) t = TK_TEMPLATE_SUFFIX; if(verbatim && CUR_CHAR == '"') { //double quotation APPEND_CHAR(CUR_CHAR); NEXT(); } else { break; } } loop_exit: TERMINATE_BUFFER(); SQInteger len = _longstr.size()-1; if(ndelim == '\'') { assert(_state != LS_TEMPLATE); if(len == 0) _ctx.reportDiagnostic(DiagnosticsId::DI_EMPTY_LITERAL, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn); if(len > 1) _ctx.reportDiagnostic(DiagnosticsId::DI_TOO_LONG_LITERAL, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn); _nvalue = _longstr[0]; return TK_INTEGER; } SetStringValue(); return t; } static void LexHexadecimal(const char *s,SQUnsignedInteger *res) { *res = 0; while(*s != 0) { if(sq_isdigit(*s)) *res = (*res)*16+((*s++)-'0'); else if(sq_isxdigit(*s)) *res = (*res)*16+(toupper(*s++)-'A'+10); else { assert(0); } } } #define INT_OVERFLOW_THRESHOLD (~SQUnsignedInteger(0) / 10) #define INT_OVERFLOW_DIGIT (~SQUnsignedInteger(0) % 10) static bool LexInteger(const char *s, SQUnsignedInteger *res) { SQUnsignedInteger x = 0; while(*s != 0) { SQUnsignedInteger digit = (*s++) - '0'; if (x > INT_OVERFLOW_THRESHOLD || (x == INT_OVERFLOW_THRESHOLD && digit > INT_OVERFLOW_DIGIT)) return false; x = x * 10 + digit; } *res = x; return x <= (~SQUnsignedInteger(0) >> 1); } static SQInteger isexponent(SQInteger c) { return c == 'e' || c=='E'; } #define MAX_HEX_DIGITS (sizeof(SQInteger)*2) #define NUM_NEXT() { do NEXT() while (CUR_CHAR=='_'); } SQInteger SQLexer::ReadNumber() { #define TINT 1 #define TFLOAT 2 #define THEX 3 #define TSCIENTIFIC 4 SQInteger type = TINT, firstchar = CUR_CHAR; INIT_TEMP_STRING(); NUM_NEXT(); if(firstchar == '0' && sq_isdigit(CUR_CHAR)) _ctx.reportDiagnostic(DiagnosticsId::DI_OCTAL_NOT_SUPPORTED, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn); if(firstchar == '0' && (toupper(CUR_CHAR) == 'X') ) { NUM_NEXT(); type = THEX; while(sq_isxdigit(CUR_CHAR)) { APPEND_CHAR(CUR_CHAR); NUM_NEXT(); } if(_longstr.size() > MAX_HEX_DIGITS) _ctx.reportDiagnostic(DiagnosticsId::DI_HEX_TOO_MANY_DIGITS, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn); if(_longstr.size() == 0) _ctx.reportDiagnostic(DiagnosticsId::DI_HEX_DIGITS_EXPECTED, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn); } else { APPEND_CHAR((char)firstchar); bool hasExp = false; bool hasDot = false; while (CUR_CHAR == '.' || sq_isalnum(CUR_CHAR)) { if(CUR_CHAR == '.') { if(hasDot || hasExp) _ctx.reportDiagnostic(DiagnosticsId::DI_MALFORMED_NUMBER, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn); hasDot = true; type = TFLOAT; } else if(isexponent(CUR_CHAR)) { if(hasExp) _ctx.reportDiagnostic(DiagnosticsId::DI_MALFORMED_NUMBER, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn); hasExp = true; type = TSCIENTIFIC; APPEND_CHAR(CUR_CHAR); NEXT(); if(CUR_CHAR == '+' || CUR_CHAR == '-'){ APPEND_CHAR(CUR_CHAR); NEXT(); } if(!sq_isdigit(CUR_CHAR)) _ctx.reportDiagnostic(DiagnosticsId::DI_FP_EXP_EXPECTED, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn); } if(!sq_isdigit(CUR_CHAR) && !isexponent(CUR_CHAR) && CUR_CHAR != '.') _ctx.reportDiagnostic(DiagnosticsId::DI_MALFORMED_NUMBER, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn); APPEND_CHAR(CUR_CHAR); NUM_NEXT(); } } TERMINATE_BUFFER(); switch(type) { case TSCIENTIFIC: case TFLOAT: #if SQ_USE_STD_FROM_CHARS { auto ret = std::from_chars(&_longstr[0], &_longstr[0] + _longstr.size(), _fvalue); if (ret.ec == std::errc::result_out_of_range) _ctx.reportDiagnostic(_fvalue == 0 ? DiagnosticsId::DI_LITERAL_UNDERFLOW : DiagnosticsId::DI_LITERAL_OVERFLOW, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn, "float"); for (const char * c = ret.ptr; c < &_longstr[0] + _longstr.size() - 1; ++c) if (*c != '0') { _ctx.reportDiagnostic(DiagnosticsId::DI_MALFORMED_NUMBER, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn); break; } } #else { char *sTemp; volatile SQFloat value; value = (SQFloat)strtod(&_longstr[0], &sTemp); _fvalue = value; if(value == 0) { for(int i = 0; i < _longstr.size(); i++) { char c = _longstr[i]; if (!c || c == 'e' || c == 'E') break; if (c != '.' && c != '0') _ctx.reportDiagnostic(DiagnosticsId::DI_LITERAL_UNDERFLOW, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn, "float"); } } } #endif if(sizeof(_fvalue) == sizeof(float)) { if (_fvalue >= FLT_MAX) _ctx.reportDiagnostic(DiagnosticsId::DI_LITERAL_OVERFLOW, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn, "float"); } else if(sizeof(_fvalue) == sizeof(double)) { if (_fvalue >= DBL_MAX) _ctx.reportDiagnostic(DiagnosticsId::DI_LITERAL_OVERFLOW, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn, "float"); } return TK_FLOAT; case TINT: if(!LexInteger(&_longstr[0],(SQUnsignedInteger *)&_nvalue)) _ctx.reportDiagnostic(DiagnosticsId::DI_LITERAL_OVERFLOW, _tokenline, _tokencolumn, _currentcolumn - _tokencolumn, "integer"); return TK_INTEGER; case THEX: LexHexadecimal(&_longstr[0],(SQUnsignedInteger *)&_nvalue); return TK_INTEGER; } return 0; } SQInteger SQLexer::ReadID() { SQInteger res; INIT_TEMP_STRING(); do { APPEND_CHAR(CUR_CHAR); NEXT(); } while(sq_isalnum(CUR_CHAR) || CUR_CHAR == '_'); TERMINATE_BUFFER(); res = GetIDType(&_longstr[0],_longstr.size() - 1); if(res == TK_IDENTIFIER || res == TK_CONSTRUCTOR) { SetStringValue(); } return res; } SQInteger SQLexer::ReadDirective() { INIT_TEMP_STRING(); do { APPEND_CHAR(CUR_CHAR); NEXT(); } while(sq_isalnum(CUR_CHAR) || CUR_CHAR == '_' || CUR_CHAR == '-' || CUR_CHAR == ':'); TERMINATE_BUFFER(); if (!_longstr[0]) return -1; SetStringValue(); return TK_DIRECTIVE; } ================================================ FILE: squirrel/compiler/lexer.h ================================================ /* see copyright notice in squirrel.h */ #ifndef _SQLEXER_H_ #define _SQLEXER_H_ #include "compilationcontext.h" #include "sourceloc.h" enum SQLexerState { LS_REGULAR, LS_TEMPLATE }; enum SQTokenFlags { TF_PREP_EOL = 1 << 0, // end of line after this token TF_PREP_SPACE = 1 << 1, // space after this token }; struct SQLexer { using LexChar = unsigned char; SQLexer(SQSharedState *ss, SQCompilation::SQCompilationContext &ctx, SQCompilation::Comments *comments); ~SQLexer(); void Init(const char *code, size_t codeSize); SQInteger Lex(); const char *Tok2Str(SQInteger tok); void SetStringValue(); private: void nextLine(); SQInteger LexSingleToken(); SQInteger GetIDType(const char *s,SQInteger len); SQInteger ReadString(SQInteger ndelim,bool verbatim, bool advance = true); SQInteger ReadNumber(); void LexBlockComment(); void LexLineComment(); SQInteger ReadID(); SQInteger ReadDirective(); void Next(); SQInteger AddUTF8(SQUnsignedInteger ch); SQInteger ProcessStringHexEscape(char *dest, SQInteger maxdigits); SQCompilation::Comments::LineCommentsList &CurLineComments() { assert(_comments); return _comments->commentsList().back(); } void AddComment(enum SQCompilation::CommentKind kind, SQInteger line, SQInteger start, SQInteger end); private: SQInteger _curtoken = -1; SQTable *_keywords = nullptr; SQBool _reached_eof = SQFalse; SQCompilation::SQCompilationContext &_ctx; const char *_sourceText = nullptr; size_t _sourceTextSize = 0; size_t _sourceTextPtr = 0; SQCompilation::Comments *_comments = nullptr; sqvector _currentComment; public: SQCompilation::SourceSpan tokenSpan() const { return { {static_cast(_tokenline), static_cast(_tokencolumn)}, {static_cast(_currentline), static_cast(_currentcolumn)} }; } SQCompilation::SourceLoc tokenStart() const { return {static_cast(_tokenline), static_cast(_tokencolumn)}; } SQCompilation::SourceLoc currentPos() const { return {static_cast(_currentline), static_cast(_currentcolumn)}; } SQInteger _prevtoken = -1; SQInteger _currentline = 1; SQInteger _lasttokenline = -1; SQInteger _lasttokencolumn = 0; SQInteger _currentcolumn = 0; SQInteger _tokencolumn = 0; SQInteger _tokenline = 1; SQInteger _expectedToken = -1; unsigned _prevflags = 0; unsigned _flags = 0; enum SQLexerState _state = LS_REGULAR; const char *_svalue = nullptr; SQInteger _nvalue = 0; SQFloat _fvalue = 0.0f; LexChar _currdata = 0; SQSharedState *_sharedstate = nullptr; sqvector _longstr; }; #endif ================================================ FILE: squirrel/compiler/optimizations/closureHoisting.cpp ================================================ /***** The basic algorithm: * Walk the AST top-down * Calculate nesting depth * Collect locals in each scope * For each closure, find the deepest scope it captures. * If the closure can be hoisted, create a VarDecl `let $chN = ` and insert it into the highest scope reachable (the one where the deepest captured variable lives). Within that scope's block, place it right before the statement that (transitively) contains the closure to ensure correct ordering with respect to imports and other declarations. * In Phase 2 (OriginalClosureReplacer) replace the original FunctionExpr with an Id reference to the generated name ($ch0, $ch1, ...) *****/ #include "closureHoisting.h" #include #include namespace SQCompilation { ClosureHoistingOpt::ClosureHoistingOpt(SQSharedState *ss, Arena *astA) : _ss(ss) , astArena(astA) , arena(ss->_alloc_ctx, "ClosureHoistingOpt") , varIdx(0) , hoistedClosures(HoistedMap::Allocator(&arena)) , funcLocals(FuncLocalsMap::Allocator(&arena)) {} const char *ClosureHoistingOpt::generateName() { char buffer[32]; int n = snprintf(buffer, sizeof(buffer), "$ch%d", varIdx++); char *result = (char *)astArena->allocate((n + 1) * sizeof(char)); strcpy(result, buffer); return result; } //------------------------------------------------------------------------------ // Capture Analysis //------------------------------------------------------------------------------ // Result of capture analysis for a single closure (internal to this file). struct CaptureInfo { int maxCaptureDepth; // Deepest captured variable's depth (-1 if none) bool capturesImmediateParent; // Does it capture from immediate parent? (blocks hoisting) int maxIndirectCaptureDepth; // Max depth with indirect capture (-1 if none) }; // Visitor that analyzes what a closure captures. // Uses a scope stack (not a flat set) to correctly handle nested function // parameter shadowing: a nested function's parameter only masks names // within that nested function, not in the outer scope. class CaptureAnalyzer : public Visitor { CaptureInfo &info; ScopeContext *funcScope; // The function being analyzed const char *selfName; // The function's name (for self-reference detection) FuncLocalsMap &funcLocals; // Pre-computed locals per function // Stack of local name sets for nested scopes. // Bottom = the function being analyzed, top = innermost nested function. // Names are checked from top to bottom; popping correctly un-shadows. ArenaVector scopeStack; bool isLocal(const char *name) { for (int i = (int)scopeStack.size() - 1; i >= 0; i--) if (scopeStack[i]->find(name) != scopeStack[i]->end()) return true; return false; } // Once the function captures from its immediate parent, it can't be hoisted // at all — stop traversing early. bool canHoist() const { return !info.capturesImmediateParent; } public: CaptureAnalyzer(CaptureInfo &i, ScopeContext *fs, const char *sn, FuncLocalsMap &fl, Arena *a) : info(i), funcScope(fs), selfName(sn), funcLocals(fl), scopeStack(a) {} void visitNode(Node *node) override { if (!canHoist()) return; node->visitChildren(this); } void visitId(Id *id) override { if (!canHoist()) return; const char *name = id->name(); // Self-reference to the function is treated as a capture from immediate parent // (where the function name is declared). This blocks hoisting. if (selfName && strcmp(name, selfName) == 0) { if (funcScope->isInImmediateParent(name)) info.capturesImmediateParent = true; return; } // Local to this function or a nested function at current nesting level if (isLocal(name)) return; // Check if captured from the function's immediate parent (blocks hoisting) if (funcScope->isInImmediateParent(name)) info.capturesImmediateParent = true; // Find capture depth relative to the function's parent int depth = funcScope->parent ? funcScope->parent->findNameDepth(name) : -1; if (depth >= 0) { info.maxCaptureDepth = std::max(info.maxCaptureDepth, depth); // Check if this capture is from a nested block (not directly in the scope's block). // Such captures prevent hoisting to that depth. if (funcScope->parent->isIndirectLocalAtDepth(name, depth)) { //-V1004 info.maxIndirectCaptureDepth = std::max(info.maxIndirectCaptureDepth, depth); } } } // Descend into nested functions: push their pre-computed locals, visit, pop. // This correctly handles parameter shadowing — a nested function's param // only masks names within that function's body. void visitFunctionExpr(FunctionExpr *f) override { if (!canHoist()) return; // Visit parameter defaults FIRST, before pushing this function's locals. // Defaults are evaluated in the enclosing scope, not the function body scope, // so body locals must not shadow outer names during default analysis. for (auto param : f->parameters()) { if (!canHoist()) break; if (param->defaultValue()) param->defaultValue()->visit(this); if (param->getDestructuring()) { for (auto decl : param->getDestructuring()->declarations()) { if (decl->expression()) decl->expression()->visit(this); } } } auto it = funcLocals.find(f); if (it != funcLocals.end()) { scopeStack.push_back(it->second); } if (canHoist()) f->body()->visit(this); if (it != funcLocals.end()) scopeStack.pop_back(); } }; //------------------------------------------------------------------------------ // Insert Hoisted Declarations //------------------------------------------------------------------------------ void ClosureHoistingOpt::insertHoistedDecls(Block *block, ArenaVector &candidates) { if (candidates.empty() || !block) return; // Sort candidates by insertion point to handle them in order std::stable_sort(candidates.begin(), candidates.end(), [](const HoistCandidate &a, const HoistCandidate &b) { return a.insertionPoint < b.insertionPoint; }); // Track how many we've inserted so far (to adjust subsequent indices) int insertionsBeforePoint = 0; for (auto &candidate : candidates) { // Generate unique name ($ch0, $ch1, etc.) candidate.generatedName = generateName(); // Record in hoistedClosures map (FunctionExpr* -> name) hoistedClosures[candidate.func] = candidate.generatedName; // Update hoisting level on FunctionExpr (for codegen) candidate.func->hoistBy(candidate.hoistDepth); // Create VarDecl with the same FunctionExpr* as initializer. // The original location still references this FunctionExpr — // OriginalClosureReplacer (Phase 2) will replace it with an Id. // // Use the insertion point statement's source position for the VarDecl, // not the original lambda position. This ensures the _OP_CLOSURE // instruction gets the correct line number for stack traces. int actualIdx = candidate.insertionPoint + insertionsBeforePoint; SourceLoc insertLoc = candidate.func->sourceSpan().start; if (actualIdx < (int)block->statements().size()) { Statement *atStmt = block->statements()[actualIdx]; // When inserting before a bare Block statement, use the first child // statement's location instead of the block's opening brace location. // The '{' brace doesn't generate instructions, so using its line would // create a spurious line entry that changes stack traces. if (atStmt->op() == TO_BLOCK) { Block *blk = static_cast(atStmt); if (!blk->statements().empty()) insertLoc = blk->statements()[0]->sourceSpan().start; else insertLoc = atStmt->sourceSpan().start; } else { insertLoc = atStmt->sourceSpan().start; } } Id *nameId = new (astArena) Id({insertLoc, insertLoc}, candidate.generatedName); VarDecl *hoistedDecl = new (astArena) VarDecl( insertLoc, nameId, candidate.func, false); // Insert into block at the correct position block->statements().insert(actualIdx, hoistedDecl); insertionsBeforePoint++; } } //------------------------------------------------------------------------------ // Typed Default Parameter Safety Check //------------------------------------------------------------------------------ // Check if a function has any parameter with both a type annotation and a // default value. The type check for such defaults happens at closure creation // time and can throw. Hoisting would move that throw to a different scope // (e.g. out of a try/catch), changing program behavior. static bool hasTypedDefaults(FunctionExpr *f) { for (auto param : f->parameters()) { if (param->getTypeMask() != ~0u && param->hasDefaultValue()) return true; } return false; } //------------------------------------------------------------------------------ // HoistingVisitor Helpers //------------------------------------------------------------------------------ // Pre-compute directLocalNames by scanning the block's direct child statements. // A local is "direct" if its declaration is a top-level statement of the block, // as opposed to being inside a for-loop init, if-body, try-body, etc. void ClosureHoistingOpt::collectDirectLocals(Block *block, ScopeContext &scope) { if (!block) return; for (auto stmt : block->statements()) { switch (stmt->op()) { case TO_VAR: scope.directLocalNames->insert(static_cast(stmt)->name()); break; case TO_CONST: scope.directLocalNames->insert(static_cast(stmt)->name()); break; case TO_DECL_GROUP: case TO_DESTRUCTURE: for (auto decl : static_cast(stmt)->declarations()) scope.directLocalNames->insert(decl->name()); break; default: break; } } } void ClosureHoistingOpt::HoistingVisitor::registerFunctionParams( FunctionExpr *f, ScopeContext &scope) { for (auto param : f->parameters()) { scope.localNames->insert(param->name()); scope.directLocalNames->insert(param->name()); if (param->getDestructuring()) { for (auto decl : param->getDestructuring()->declarations()) { scope.localNames->insert(decl->name()); scope.directLocalNames->insert(decl->name()); } } } } void ClosureHoistingOpt::HoistingVisitor::tryHoistFunction( FunctionExpr *f, ScopeContext &funcScope) { // Don't hoist top-level functions or class methods ScopeContext *parentScope = funcScope.parent; if (!parentScope || !parentScope->parent) return; if (funcScope.isClassMethod()) return; // Don't hoist const functions - they are compile-time constants if (insideConstDecl) return; // Don't hoist functions with typed default parameters. // The type check for defaults happens at closure creation and can throw. // Hoisting moves the closure creation to a different scope, which can // change error handling behavior (e.g. moving it out of a try/catch). if (hasTypedDefaults(f)) return; // Analyze what this closure captures CaptureInfo captures = {-1, false, -1}; CaptureAnalyzer analyzer(captures, &funcScope, f->name(), owner->funcLocals, &owner->arena); analyzer.visitFunctionExpr(f); if (captures.capturesImmediateParent) return; // Compute target depth based on captures int targetDepth; if (captures.maxCaptureDepth < 0) { targetDepth = 0; // No captures — hoist all the way to root } else { // Can't hoist above the deepest capture. // Indirect captures (in nested blocks) require staying one level deeper. targetDepth = captures.maxCaptureDepth; if (captures.maxIndirectCaptureDepth >= 0) targetDepth = std::max(targetDepth, captures.maxIndirectCaptureDepth + 1); } if (targetDepth >= funcScope.depth) return; // Walk up to the target scope ScopeContext *target = parentScope; while (target && target->depth > targetDepth) target = target->parent; if (target && target->localCount() < ScopeContext::MAX_LOCALS_FOR_HOISTING) { int hoistDepth = funcScope.depth - targetDepth; target->pendingHoists.push_back({f, hoistDepth, nullptr, target->currentStatementIndex}); } } //------------------------------------------------------------------------------ // HoistingVisitor Implementation //------------------------------------------------------------------------------ void ClosureHoistingOpt::HoistingVisitor::visitBlock(Block *b) { bool isOwnBlock = currentScope && currentScope->block == b; int savedIndex = currentScope ? currentScope->currentStatementIndex : 0; for (int i = 0; i < (int)b->statements().size(); ++i) { if (isOwnBlock) currentScope->currentStatementIndex = i; b->statements()[i]->visit(this); } if (isOwnBlock) currentScope->currentStatementIndex = savedIndex; } void ClosureHoistingOpt::HoistingVisitor::visitFunctionExpr(FunctionExpr *f) { ScopeContext newScope(&owner->arena, currentScope, /*isClassScope=*/false, f->body()); registerFunctionParams(f, newScope); collectDirectLocals(f->body(), newScope); ScopeContext *prevScope = currentScope; currentScope = &newScope; for (auto param : f->parameters()) param->visit(this); f->body()->visit(this); currentScope = prevScope; // Save locals for CaptureAnalyzer (zero-copy pointer sharing) owner->funcLocals[f] = newScope.localNames; newScope.enforceLocalsLimit(); owner->insertHoistedDecls(f->body(), newScope.pendingHoists); tryHoistFunction(f, newScope); } void ClosureHoistingOpt::HoistingVisitor::visitClassExpr(ClassExpr *c) { if (c->classBase()) c->classBase()->visit(this); ScopeContext newScope(&owner->arena, currentScope, /*isClassScope=*/true, nullptr); ScopeContext *prevScope = currentScope; currentScope = &newScope; if (c->classKey() && c->classKey()->op() != TO_ID) c->classKey()->visit(this); for (auto &member : c->members()) { if (member.key) member.key->visit(this); if (member.value) member.value->visit(this); } currentScope = prevScope; } void ClosureHoistingOpt::HoistingVisitor::visitVarDecl(VarDecl *v) { currentScope->localNames->insert(v->name()); if (v->expression()) v->expression()->visit(this); } void ClosureHoistingOpt::HoistingVisitor::visitParamDecl(ParamDecl *p) { if (p->defaultValue()) p->defaultValue()->visit(this); if (p->getDestructuring()) { for (auto decl : p->getDestructuring()->declarations()) { if (decl->expression()) decl->expression()->visit(this); } } } void ClosureHoistingOpt::HoistingVisitor::visitConstDecl(ConstDecl *c) { currentScope->localNames->insert(c->name()); if (c->value()) { bool wasInConstDecl = insideConstDecl; insideConstDecl = true; c->value()->visit(this); insideConstDecl = wasInConstDecl; } } void ClosureHoistingOpt::HoistingVisitor::visitTryStatement(TryStatement *stmt) { stmt->tryStatement()->visit(this); currentScope->localNames->insert(stmt->exceptionId()->name()); stmt->catchStatement()->visit(this); } void ClosureHoistingOpt::HoistingVisitor::visitForeachStatement(ForeachStatement *fe) { if (fe->idx()) currentScope->localNames->insert(fe->idx()->name()); if (fe->val()) currentScope->localNames->insert(fe->val()->name()); fe->container()->visit(this); fe->body()->visit(this); } //------------------------------------------------------------------------------ // OriginalClosureReplacer Implementation //------------------------------------------------------------------------------ Node *ClosureHoistingOpt::OriginalClosureReplacer::transformFunctionExpr(FunctionExpr *f) { auto it = hoistedClosures.find(f); if (it != hoistedClosures.end()) { // First encounter is the hoisted VarDecl initializer — keep it. // Second encounter is the original location — replace with Id. if (resolved.find(f) == resolved.end()) { resolved.insert(f); f->transformChildren(this); return f; } const char *hoistedName = it->second; return new (astArena) Id(f->sourceSpan(), hoistedName); } f->transformChildren(this); return f; } //------------------------------------------------------------------------------ // Main Entry Point //------------------------------------------------------------------------------ void ClosureHoistingOpt::run(RootBlock *root) { if (!_ss->checkCompilationOption(CompilationOptions::CO_CLOSURE_HOISTING_OPT)) return; // Phase 1: Traverse AST, collect scope info, and insert hoisted VarDecls. // After this, each hoisted FunctionExpr is referenced from two places: // the new VarDecl (at the target scope) and the original location. ScopeContext rootScope(&arena, nullptr, /*isClassScope=*/false, root); collectDirectLocals(root, rootScope); HoistingVisitor visitor(this, &rootScope); root->visit(&visitor); rootScope.enforceLocalsLimit(); insertHoistedDecls(root, rootScope.pendingHoists); // Phase 2: Resolve the duplication — replace original FunctionExpr // occurrences with Id references to the hoisted variable names. if (!hoistedClosures.empty()) { OriginalClosureReplacer transformer(hoistedClosures, &arena, astArena); root->transform(&transformer); } } } // namespace SQCompilation ================================================ FILE: squirrel/compiler/optimizations/closureHoisting.h ================================================ #pragma once #include "../ast.h" #include "../arena.h" #include "../sqstate.h" namespace SQCompilation { // TODO: Unify this with functions from static analyzer struct str_hash { size_t operator()(const char *s) const { size_t h = 0; for (; *s; ++s) h = h * 31 + (unsigned char)*s; return h; } }; struct str_eq { bool operator()(const char *a, const char *b) const { return std::strcmp(a, b) == 0; } }; typedef ArenaUnorderedSet NameSet; // Represents a closure that CAN be hoisted struct HoistCandidate { FunctionExpr *func; int hoistDepth; // How many levels to hoist const char *generatedName; // $ch0, $ch1, etc. (assigned when hoisting) int insertionPoint; // Index in target block where to insert (before this statement) }; // Function/class-level scope context (NOT per-block). Created for: root scope, // each FunctionExpr, and each ClassExpr. Block-level constructs (for, foreach, // try, if, while) do NOT create their own ScopeContext — their variables are // tracked in the enclosing function's localNames. The directLocalNames subset // distinguishes top-level declarations from those inside nested blocks. // The localNames/directLocalNames sets are arena-allocated and survive scope exit, // allowing CaptureAnalyzer to share them via pointer (zero-copy). struct ScopeContext { ScopeContext *parent; // null for root scope bool isClassScope; // true only for class body (not methods inside it) int depth; // Nesting depth (class scopes don't increment) Block *block; // The block where hoisted decls go int currentStatementIndex; // Index of statement currently being visited in this scope's block // ALL locals declared in this function scope (params, vars, for-loop inits, // foreach vars, try exception vars). Used for capture depth calculation. // Arena-allocated pointer: survives scope exit for CaptureAnalyzer reuse. NameSet *localNames; // Subset of localNames: only locals declared as direct children of this // scope's block (params + top-level let/const/destructuring). Excludes // for-loop init vars, foreach vars, try exception vars, etc. // Used by isIndirectLocalAtDepth() to detect captures from nested blocks. NameSet *directLocalNames; // Candidates found in child scopes that should hoist to THIS level ArenaVector pendingHoists; static NameSet *makeNameSet(Arena *arena) { void *mem = arena->allocate(sizeof(NameSet)); return new (mem) NameSet(NameSet::Allocator(arena)); } // Constructor computes depth, skipping class scopes ScopeContext(Arena *arena, ScopeContext *p, bool isClass, Block *b) : parent(p), isClassScope(isClass), depth(p ? p->depth + (p->isClassScope ? 0 : 1) : 0), block(b), currentStatementIndex(0), localNames(makeNameSet(arena)), directLocalNames(makeNameSet(arena)), pendingHoists(arena) {} int findNameDepth(const char *name) const { if (localNames->find(name) != localNames->end()) return depth; if (parent) return parent->findNameDepth(name); return -1; // Not found (global/builtin) } bool isInImmediateParent(const char *name) const { if (!parent) return false; return parent->localNames->find(name) != parent->localNames->end(); } // Check if a name at a given depth is in a nested block (not directly in that scope's block). // Returns true if the name is a local at that depth but NOT a direct block declaration. bool isIndirectLocalAtDepth(const char *name, int targetDepth) const { // Walk up to scope at targetDepth const ScopeContext *scope = this; while (scope && scope->depth > targetDepth) { scope = scope->parent; } if (!scope || scope->depth != targetDepth) { return false; // Couldn't find scope at this depth } // Check if name is in scope's localNames but NOT in directLocalNames if (scope->localNames->find(name) != scope->localNames->end()) { return scope->directLocalNames->find(name) == scope->directLocalNames->end(); } return false; } bool isClassMethod() const { return parent && parent->isClassScope; } // Count of locals including pending hoists (for frame size limit) size_t localCount() const { return localNames->size() + pendingHoists.size(); } // Leave some room for temporary variables static const size_t MAX_LOCALS_FOR_HOISTING = 200; // Trim pending hoists if adding them would exceed the locals limit void enforceLocalsLimit() { int maxHoists = std::max(0, (int)MAX_LOCALS_FOR_HOISTING - (int)localNames->size()); while ((int)pendingHoists.size() > maxHoists) { pendingHoists.pop_back(); } } }; // Pre-computed locals per function: FunctionExpr* -> its localNames set. // Populated during HoistingVisitor, consumed by CaptureAnalyzer. // Shares the same arena-allocated NameSet pointers from ScopeContext. typedef ArenaUnorderedMap FuncLocalsMap; class ClosureHoistingOpt { public: ClosureHoistingOpt(SQSharedState *ss, Arena *astArena); void run(RootBlock *root); private: SQSharedState *_ss; Arena *astArena; // For AST node allocation Arena arena; // Internal arena for temporary data structures int varIdx; // Counter for generating $ch0, $ch1, etc. // FunctionExpr* -> generated name mapping typedef ArenaUnorderedMap HoistedMap; HoistedMap hoistedClosures; FuncLocalsMap funcLocals; // Pre-compute which locals are declared directly in a block (not in nested // for/if/try/while blocks). static void collectDirectLocals(Block *block, ScopeContext &scope); // Single-pass visitor that collects local names, evaluates hoisting, // and inserts hoisted VarDecls at scope boundaries class HoistingVisitor : public Visitor { ClosureHoistingOpt *owner; ScopeContext *currentScope; bool insideConstDecl = false; void registerFunctionParams(FunctionExpr *f, ScopeContext &scope); void tryHoistFunction(FunctionExpr *f, ScopeContext &funcScope); public: HoistingVisitor(ClosureHoistingOpt *o, ScopeContext *root) : owner(o), currentScope(root) {} void visitBlock(Block *b) override; void visitFunctionExpr(FunctionExpr *f) override; void visitClassExpr(ClassExpr *c) override; void visitVarDecl(VarDecl *v) override; void visitParamDecl(ParamDecl *p) override; void visitConstDecl(ConstDecl *c) override; void visitTryStatement(TryStatement *stmt) override; void visitForeachStatement(ForeachStatement *fe) override; }; // After Phase 1, each hoisted FunctionExpr is referenced from two places: // the new VarDecl (at the target scope) and the original location. // This transformer resolves the duplication by replacing the original // occurrence with an Id reference to the hoisted variable. class OriginalClosureReplacer : public Transformer { HoistedMap &hoistedClosures; ArenaUnorderedSet resolved; // Hoisted FunctionExprs already resolved (kept at VarDecl) Arena *astArena; public: OriginalClosureReplacer(HoistedMap &map, Arena *arena, Arena *astA) : hoistedClosures(map), resolved(decltype(resolved)::Allocator(arena)), astArena(astA) {} Node *transformFunctionExpr(FunctionExpr *f) override; }; // Helper methods const char *generateName(); void insertHoistedDecls(Block *block, ArenaVector &candidates); }; } // namespace SQCompilation ================================================ FILE: squirrel/compiler/optimizer.cpp ================================================ #include "sqpcheader.h" #include "sqstring.h" #include "sqfuncproto.h" #include "sqtable.h" #include "opcodes.h" #include "sqfuncstate.h" #include "optimizer.h" #include "sq_safe_shift.h" SQOptimizer::SQOptimizer(SQFuncState & func_state) : fs(&func_state), jumps(func_state._full_line_infos._alloc_ctx), codeChanged(false) {} #undef _DEBUG_DUMP #ifdef _DEBUG_DUMP void SQOptimizer::debugPrintInstructionPos(const char * message, int instructionIndex) { for (int i = 0; i < fs->_full_line_infos.size(); i++) if (fs->_full_line_infos[i]._op >= instructionIndex) { printf("OPTIMIZER: %s %s:%d\n", message, _stringval(fs->_sourcename), fs->_full_line_infos[i]._line); return; } if (fs->_full_line_infos.size() > 0) printf("OPTIMIZER: %s %s:%d\n", message, _stringval(fs->_sourcename), fs->_full_line_infos.top()._line); } #endif bool SQOptimizer::isUnsafeJumpRange(int start, int count) const { for (int i = 0, ie = jumps.size(); i < ie; i++) { int to = jumps[i].jumpTo; if (to > start && to < start + count) return true; } return false; } bool SQOptimizer::isLocalVarInstructions(int start, int count) const { for (int i = 0, ie = fs->_localvarinfos.size(); i < ie; i++) { int pos = int(fs->_localvarinfos[i]._start_op); if (pos > start && pos < start + count) return true; } return false; } bool SQOptimizer::isUnsafeRange(int start, int count) const { return isUnsafeJumpRange(start, count) || isLocalVarInstructions(start, count); } bool SQOptimizer::isLocalVarRegister(int reg, int instrIndex) const { for (int i = 0, ie = fs->_localvarinfos.size(); i < ie; i++) { const SQLocalVarInfo & v = fs->_localvarinfos[i]; if ((int)v._pos == reg && (int)v._start_op <= instrIndex && (v._end_op == UINT32_MINUS_ONE || (int)v._end_op > instrIndex)) return true; } return false; } void SQOptimizer::cutRange(int start, int old_count, int new_count) { assert(new_count < old_count); int tmpStart = start + new_count; int tmpCount = old_count - new_count; //printf("cut: start=%d count=%d\n", tmpStart, tmpCount); for (int i = 0; i < jumps.size(); i++) { if (jumps[i].jumpTo >= tmpStart + 1) { jumps[i].jumpTo -= tmpCount; jumps[i].modified = true; } if (jumps[i].instructionIndex >= tmpStart) { jumps[i].instructionIndex -= tmpCount; jumps[i].modified = true; } } memmove(&fs->_instructions[tmpStart], &fs->_instructions[tmpStart + tmpCount], sizeof(fs->_instructions[0]) * (fs->_instructions.size() - tmpStart - tmpCount)); fs->_instructions.resize(fs->_instructions.size() - tmpCount); for (int i = 0; i < fs->_localvarinfos.size(); i++) { SQLocalVarInfo & varinfo = fs->_localvarinfos[i]; if (varinfo._start_op > tmpStart) { if (varinfo._end_op < varinfo._start_op) { varinfo._end_op = varinfo._start_op - tmpCount - 1; if (varinfo._end_op > tmpStart) varinfo._end_op = 0; } varinfo._start_op -= tmpCount; } if (varinfo._end_op >= tmpStart && varinfo._end_op != UINT32_MINUS_ONE) { int n = int(varinfo._end_op) - tmpCount; if (n < tmpStart - 1) n = tmpStart - 1; if (n < 0) n = 0; varinfo._end_op = n; } } for (int i = 0; i < fs->_full_line_infos.size(); i++) if (fs->_full_line_infos[i]._op > tmpStart) fs->_full_line_infos[i]._op -= tmpCount; codeChanged = true; } void SQOptimizer::optimizeConstFolding() { SQInstructionVec & instr = fs->_instructions; bool changed = false; for (int i = 0; i + 2 < instr.size(); i++) { do { changed = false; if (i + 3 < instr.size()) { const SQInstruction & operation = instr[i + 2]; const SQInstruction & loadA = instr[i]; const SQInstruction & loadB = instr[i + 1]; int s = operation.op; if ((s == _OP_ADD || s == _OP_SUB || s == _OP_MUL || s == _OP_DIV || s == _OP_MOD || s == _OP_BITW) && (loadA.op == _OP_LOADINT || loadA.op == _OP_LOADFLOAT) && (loadB.op == _OP_LOADINT || loadB.op == _OP_LOADFLOAT) && loadA._arg0 != loadB._arg0 && ((loadB._arg0 == operation._arg2 && loadA._arg0 == operation._arg1) || (loadB._arg0 == operation._arg1 && loadA._arg0 == operation._arg2)) && !isUnsafeJumpRange(i, 3)) { bool applyOpt = true; const bool reversed = (loadB._arg0 == operation._arg2 && loadA._arg0 == operation._arg1); bool removeLoadAVar = false, removeLoadBVar = false; if (!isLocalVarInstructions(i, 3)) removeLoadAVar = removeLoadBVar = true; else if (!isLocalVarInstructions(i, 2)) removeLoadAVar = true; else if (!isLocalVarInstructions(i + 1, 2)) removeLoadBVar = true; if (removeLoadAVar && isLocalVarRegister(loadA._arg0, i)) removeLoadAVar = false; if (removeLoadBVar && isLocalVarRegister(loadB._arg0, i + 1)) removeLoadBVar = false; if (removeLoadAVar && !removeLoadBVar) removeLoadAVar = false; const int targetInst = removeLoadAVar ? i : removeLoadBVar ? i + 1 : i + 2; if (loadA.op == _OP_LOADINT && loadB.op == _OP_LOADINT) { SQInteger res = 0; SQInt32 lv = loadA._arg1; SQInt32 rv = loadB._arg1; if (reversed) { SQInt32 t = lv; lv = rv; rv = t; } switch (s) { case _OP_ADD: res = SQInteger(lv) + SQInteger(rv); break; case _OP_SUB: res = SQInteger(lv) - SQInteger(rv); break; case _OP_MUL: res = SQInteger(lv) * SQInteger(rv); break; case _OP_DIV: if (rv < -1 || rv > 0) res = lv / rv; else applyOpt = false; break; case _OP_MOD: if (rv < -1 || rv > 0) res = lv % rv; else applyOpt = false; break; case _OP_BITW: switch (operation._arg3) { case BW_AND: res = lv & rv; break; case BW_OR: res = lv | rv; break; case BW_XOR: res = lv ^ rv; break; case BW_SHIFTL: res = sq_safe_shift_left(SQInteger(lv), rv); break; default: applyOpt = false; break; } break; default: applyOpt = false; break; } if (applyOpt) { instr[targetInst]._arg0 = operation._arg0; if (res < SQInteger(INT_MIN) || res > SQInteger(INT_MAX)) { instr[targetInst].op = _OP_LOAD; instr[targetInst]._arg1 = fs->GetNumericConstant(res); } else { instr[targetInst].op = _OP_LOADINT; instr[targetInst]._arg1 = (SQInt32)res; } changed = true; codeChanged = true; #ifdef _DEBUG_DUMP debugPrintInstructionPos("Const folding", i); #endif } } else { // float assert(sizeof(SQFloat) == sizeof(SQInt32)); SQFloat res = 0; SQFloat lv = (loadA.op == _OP_LOADFLOAT) ? loadA._farg1 : SQFloat(loadA._arg1); SQFloat rv = (loadB.op == _OP_LOADFLOAT) ? loadB._farg1 : SQFloat(loadB._arg1); if (reversed) { SQFloat t = lv; lv = rv; rv = t; } switch (s) { case _OP_ADD: res = lv + rv; break; case _OP_SUB: res = lv - rv; break; case _OP_MUL: res = lv * rv; break; case _OP_DIV: if (rv != 0) res = lv / rv; else applyOpt = false; break; default: applyOpt = false; break; } if (applyOpt) { instr[targetInst].op = _OP_LOADFLOAT; instr[targetInst]._farg1 = res; instr[targetInst]._arg0 = operation._arg0; changed = true; codeChanged = true; #ifdef _DEBUG_DUMP debugPrintInstructionPos("Const folding", i); #endif } } if (applyOpt && (removeLoadAVar || removeLoadBVar)) { if (removeLoadAVar && removeLoadBVar) cutRange(i, 3, 1); else { if (removeLoadAVar) cutRange(i, 1, 0); cutRange(i + 1, 2, 1); } } } } if (i + 2 < instr.size()) { const SQInstruction operation = instr[i + 1]; const SQInstruction loadA = instr[i]; int s = operation.op; if (s == _OP_ADDI && (loadA.op == _OP_LOADINT || loadA.op == _OP_LOADFLOAT) && loadA._arg0 == operation._arg2 && !isUnsafeJumpRange(i, 2)){ bool applyOpt = true; bool removeLoadAVar = !isLocalVarInstructions(i, 2); if (removeLoadAVar && isLocalVarRegister(loadA._arg0, i)) removeLoadAVar = false; const int targetInst = removeLoadAVar ? i : i + 1; if (loadA.op == _OP_LOADINT) { SQInteger res = 0; SQInt32 lv = loadA._arg1; SQInt32 rv = operation._arg1; switch (s) { // -V785 case _OP_ADDI: res = SQInteger(lv) + SQInteger(rv); break; default: applyOpt = false; break; } if (applyOpt) { // -V547 instr[targetInst]._arg0 = operation._arg0; if (res < SQInteger(INT_MIN) || res > SQInteger(INT_MAX)) { instr[targetInst].op = _OP_LOAD; instr[targetInst]._arg1 = fs->GetNumericConstant(res); } else { instr[targetInst].op = _OP_LOADINT; instr[targetInst]._arg1 = (SQInt32)res; } changed = true; codeChanged = true; #ifdef _DEBUG_DUMP debugPrintInstructionPos("Const folding", i); #endif } } else { // float assert(sizeof(SQFloat) == sizeof(SQInt32)); SQFloat res = 0; SQFloat lv = loadA._farg1; SQFloat rv = SQFloat(operation._arg1); switch (s) { // -V785 case _OP_ADDI: res = lv + rv; break; default: applyOpt = false; break; } if (applyOpt) { // -V547 instr[targetInst].op = _OP_LOADFLOAT; instr[targetInst]._farg1 = res; instr[targetInst]._arg0 = operation._arg0; changed = true; codeChanged = true; #ifdef _DEBUG_DUMP debugPrintInstructionPos("Const folding", i); #endif } } if (applyOpt && removeLoadAVar) // -V547 cutRange(i, 2, 1); } if (s == _OP_JCMP && (loadA.op == _OP_LOADINT || loadA.op == _OP_LOADFLOAT || loadA.op == _OP_LOAD) && loadA._arg0 == operation._arg0 && loadA._arg0 != operation._arg2 && ( loadA._arg1 <= 255 || (loadA.op != _OP_LOAD && operation._arg1 >= -128 && operation._arg1 <= 127) ) && !isUnsafeJumpRange(i, 2)) { bool removeLoadAVar = !isLocalVarInstructions(i, 2); if (removeLoadAVar && isLocalVarRegister(loadA._arg0, i)) removeLoadAVar = false; const int targetInst = removeLoadAVar ? i : i + 1; bool applyOpt = true; if (loadA.op == _OP_LOAD) { instr[targetInst]._arg1 = operation._arg1; instr[targetInst]._arg2 = operation._arg2; instr[targetInst]._arg3 = operation._arg3; instr[targetInst]._arg0 = loadA._arg1; instr[targetInst].op = _OP_JCMPK; } else if (operation._arg1 < -128 || operation._arg1 > 127) // we can't fit into JCMP(I|F) due to big jump distance { const uint32_t constI = fs->GetConstant(loadA.op == _OP_LOADFLOAT ? SQObjectPtr(loadA._farg1) : SQObjectPtr((SQInteger)loadA._arg1), 255); if (constI <= 255u) { instr[targetInst]._arg1 = operation._arg1; instr[targetInst]._arg2 = operation._arg2; instr[targetInst]._arg3 = operation._arg3; instr[targetInst]._arg0 = constI; instr[targetInst].op = _OP_JCMPK; } else applyOpt = false; } else { instr[targetInst]._arg3 = operation._arg3; // cmp type instr[targetInst]._arg1 = loadA._arg1; // compare value instr[targetInst]._arg2 = operation._arg2; instr[targetInst]._arg0 = operation._arg1; // jump target instr[targetInst].op = loadA.op == _OP_LOADINT ? _OP_JCMPI : _OP_JCMPF; } if (applyOpt) { if (removeLoadAVar) cutRange(i, 2, 1); const bool convertJumpTarget = instr[targetInst].op == _OP_JCMPI || instr[targetInst].op == _OP_JCMPF; if (convertJumpTarget) for (int ji = 0, jie = jumps.size(); ji < jie; ji++) { if (jumps[ji].instructionIndex == targetInst) { assert(jumps[ji].jumpArg == JumpArg::JUMP_ARG1); jumps[ji].jumpArg = JumpArg::JUMP_ARG0; break; } } changed = true; codeChanged = true; #ifdef _DEBUG_DUMP debugPrintInstructionPos("Jump Const folding", i); #endif } } } } while (changed && i + 2 < instr.size()); } } void SQOptimizer::optimizeJumpFolding() { SQInstructionVec & instr = fs->_instructions; bool changed = true; for (int pass = 0; pass < 4 && changed; pass++) { changed = false; for (int i = 0; i < instr.size(); i++) { int op = instr[i].op; if (op == _OP_JMP || op == _OP_JCMP || op == _OP_JCMPK || op == _OP_JZ || op == _OP_AND || op == _OP_OR || op == _OP_PUSHTRAP) { int to = (i + instr[i]._arg1 + 1); if (instr[to].op == _OP_JMP) { changed = true; codeChanged = true; instr[i]._arg1 += instr[to]._arg1 + 1; #ifdef _DEBUG_DUMP debugPrintInstructionPos("Jump folding", i); #endif } } if (op == _OP_JCMPI || op == _OP_JCMPF) { int to = (i + instr[i]._sarg0() + 1); if (instr[to].op == _OP_JMP) { const int nextJumpOfs = instr[i]._sarg0() + instr[to]._arg1 + 1; if (nextJumpOfs >= -128 && nextJumpOfs <= 127) { changed = true; codeChanged = true; instr[i]._arg0 = nextJumpOfs; #ifdef _DEBUG_DUMP debugPrintInstructionPos("Jump folding", i); #endif } } } } } } void SQOptimizer::optimizeEmptyJumps() { SQInstructionVec & instr = fs->_instructions; if (!instr.size()) return; for (int i = instr.size() - 1; i > 0; i--) { int op = instr[i].op; if (op == _OP_JMP && instr[i]._arg1 == 0) { codeChanged = true; cutRange(i, 1, 0); #ifdef _DEBUG_DUMP debugPrintInstructionPos("Empty jump", i); #endif } } } void SQOptimizer::optimize() { codeChanged = true; for (int pass = 0; pass < 2 && codeChanged; pass++) { jumps.resize(0); SQInstructionVec & instr = fs->_instructions; for (int i = 0; i < instr.size(); i++) switch (instr[i].op) { case _OP_JMP: case _OP_JCMP: case _OP_JCMPK: case _OP_JZ: case _OP_AND: case _OP_OR: case _OP_PUSHTRAP: case _OP_FOREACH: case _OP_PREFOREACH: case _OP_POSTFOREACH: jumps.push_back({i, i, i + instr[i]._arg1 + 1, i + instr[i]._arg1 + 1, false, JumpArg::JUMP_ARG1}); break; case _OP_JCMPI: case _OP_JCMPF: jumps.push_back({i, i, i + instr[i]._sarg0() + 1, i + instr[i]._sarg0() + 1, false, JumpArg::JUMP_ARG0}); break; case _OP_NULLCOALESCE: jumps.push_back({i, i, i + instr[i]._arg1, i + instr[i]._arg1, false, JumpArg::JUMP_ARG1}); break; case _OP_DATA_NOP: { int saveInstr = i + ((instr[i]._arg2 << 8) + instr[i]._arg3); if ((instr[i]._arg0 || instr[i]._arg1 || instr[i]._arg2 || instr[i]._arg3) && saveInstr > 0 && saveInstr < instr.size() && instr[saveInstr].op == _OP_SAVE_STATIC_MEMO) { int loadInstr = saveInstr + 1 - ((instr[saveInstr]._arg2 << 8) + instr[saveInstr]._arg3); if (loadInstr == i) jumps.push_back({i, i, saveInstr + 1, saveInstr + 1, false, JumpArg::JUMP_ARG_PLUS_23}); } } break; case _OP_SAVE_STATIC_MEMO: { int loadInstr = i + 1 - ((instr[i]._arg2 << 8) + instr[i]._arg3); jumps.push_back({i, i, loadInstr, loadInstr, false, JumpArg::JUMP_ARG_MINUS_23}); } break; default: break; } codeChanged = false; optimizeConstFolding(); optimizeEmptyJumps(); if (codeChanged) for (int i = 0; i < jumps.size(); i++) if (jumps[i].modified) { const int change = (jumps[i].jumpTo - jumps[i].originalJumpTo) - (jumps[i].instructionIndex - jumps[i].originalInstructionIndex); if (jumps[i].jumpArg == JumpArg::JUMP_ARG1) instr[jumps[i].instructionIndex]._arg1 += change; else if (jumps[i].jumpArg == JumpArg::JUMP_ARG0) { const SQInstruction originalJump = instr[jumps[i].instructionIndex]; const int nextJumpVal = originalJump._sarg0() + change; if (nextJumpVal >= -128 && nextJumpVal <= 127) { instr[jumps[i].instructionIndex]._arg0 = nextJumpVal; // still fit } else if (instr[jumps[i].instructionIndex].op == _OP_JCMPF || instr[jumps[i].instructionIndex].op == _OP_JCMPI) { const uint32_t constI = fs->GetConstant(instr[jumps[i].instructionIndex].op == _OP_JCMPF ? SQObjectPtr(originalJump._farg1) : SQObjectPtr((SQInteger)originalJump._arg1), 255); if (constI <= 255u) // we still fit in const table { jumps[i].jumpArg = JumpArg::JUMP_ARG1; instr[jumps[i].instructionIndex]._arg1 = nextJumpVal; instr[jumps[i].instructionIndex]._arg0 = constI; instr[jumps[i].instructionIndex].op = _OP_JCMPK; } else assert(0);//todo: we need to convert back to OP_JCMP, and we need to generate load instruction for that } else assert(0);//todo: we need to convert back to OP_JCMP, and we need to generate load instruction for that } else if (jumps[i].jumpArg == JumpArg::JUMP_ARG_PLUS_23) { SQInstruction & ci = instr[jumps[i].instructionIndex]; int jmp = (ci._arg2 << 8) + ci._arg3; jmp += change; assert(jmp > 0 && jmp < 0xFFFF); ci._arg2 = jmp >> 8; ci._arg3 = jmp & 0xFF; } else if (jumps[i].jumpArg == JumpArg::JUMP_ARG_MINUS_23) { SQInstruction & ci = instr[jumps[i].instructionIndex]; int jmp = (ci._arg2 << 8) + ci._arg3; jmp -= change; assert(jmp > 0 && jmp < 0xFFFF); ci._arg2 = jmp >> 8; ci._arg3 = jmp & 0xFF; } else assert(0); } } optimizeJumpFolding(); #ifdef _DEBUG_DUMP for (int i = 0; i < jumps.size(); i++) printf("JUMPS: instruction: %d to: %d\n", jumps[i].instructionIndex, jumps[i].jumpTo); #endif } ================================================ FILE: squirrel/compiler/optimizer.h ================================================ /* see copyright notice in squirrel.h */ #ifndef _SQOPTIMIZER_H_ #define _SQOPTIMIZER_H_ /////////////////////////////////// #include "sqfuncstate.h" struct SQOptimizer { SQOptimizer(SQFuncState & func_state); void optimize(); private: bool isUnsafeRange(int start, int count) const; bool isUnsafeJumpRange(int start, int count) const; bool isLocalVarInstructions(int start, int count) const; bool isLocalVarRegister(int reg, int instrIndex) const; void cutRange(int start, int old_count, int new_count); void optimizeConstFolding(); void optimizeJumpFolding(); void optimizeEmptyJumps(); enum class JumpArg : uint8_t {JUMP_ARG1, JUMP_ARG0, JUMP_ARG_PLUS_23, JUMP_ARG_MINUS_23}; struct Jump { int originalInstructionIndex; int instructionIndex; int originalJumpTo; int jumpTo; bool modified; JumpArg jumpArg; }; sqvector jumps; SQFuncState * fs; bool codeChanged; #ifdef _DEBUG_DUMP void debugPrintInstructionPos(const char * message, int instructionIndex); #endif }; #endif // _SQOPTIMIZER_H_ ================================================ FILE: squirrel/compiler/parser.cpp ================================================ #include "sqpcheader.h" #ifndef NO_COMPILER #include "opcodes.h" #include "sqstring.h" #include "sqfuncproto.h" #include "parser.h" #include "compiler.h" #include "compilationcontext.h" #include "sqtypeparser.h" #include #include namespace SQCompilation { struct NestingChecker { SQParser *_p; const uint32_t _max_depth; uint32_t _depth; NestingChecker(SQParser *p) : _p(p), _depth(0), _max_depth(500) { inc(); } ~NestingChecker() { _p->_depth -= _depth; } void inc() { if (_p->_depth > _max_depth) { _p->reportDiagnostic(DiagnosticsId::DI_TOO_BIG_AST); } _p->_depth += 1; _depth += 1; } }; SQParser::SQParser(SQVM *v, const char *sourceText, size_t sourceTextSize, const char* sourcename, Arena *astArena, SQCompilationContext &ctx, Comments *comments) : _lex(_ss(v), ctx, comments) , _ctx(ctx) , _astArena(astArena) , _docObjectStack(astArena) { _vm=v; _lex.Init(sourceText, sourceTextSize); _sourcename = sourcename; _expression_context = SQE_REGULAR; _lang_features = _ss(v)->defaultLangFeatures; _depth = 0; _token = 0; _docObjectStack.push_back(&_moduleDocObject); } void SQParser::reportDiagnostic(int32_t id, ...) { va_list vargs; va_start(vargs, id); SourceSpan span = _lex.tokenSpan(); _ctx.vreportDiagnostic((enum DiagnosticsId)id, span.start.line, span.start.column, span.textWidth(), vargs); va_end(vargs); } bool SQParser::ProcessPosDirective() { const char *sval = _lex._svalue; if (strncmp(sval, "pos:", 4) != 0) return false; sval += 4; if (!sq_isdigit(*sval)) reportDiagnostic(DiagnosticsId::DI_EXPECTED_LINENUM); char * next = NULL; _lex._currentline = scstrtol(sval, &next, 10); if (!next || *next != ':') { reportDiagnostic(DiagnosticsId::DI_EXPECTED_TOKEN, ":"); return false; } next++; if (!sq_isdigit(*next)) reportDiagnostic(DiagnosticsId::DI_EXPECTED_COLNUM); _lex._currentcolumn = scstrtol(next, NULL, 10); return true; } struct SQPragmaDescriptor { const char *id; SQInteger setFlags, clearFlags; }; static const SQPragmaDescriptor pragmaDescriptors[] = { { "strict", LF_STRICT, 0 }, { "relaxed", 0, LF_STRICT }, { "forbid-root-table", LF_FORBID_ROOT_TABLE, 0 }, { "allow-root-table", 0, LF_FORBID_ROOT_TABLE }, { "disable-optimizer", LF_DISABLE_OPTIMIZER, 0 }, { "enable-optimizer", 0, LF_DISABLE_OPTIMIZER }, { "forbid-delete-operator", LF_FORBID_DELETE_OP, 0 }, { "allow-delete-operator", 0, LF_FORBID_DELETE_OP }, { "forbid-clone-operator", LF_FORBID_CLONE_OP, 0 }, { "allow-clone-operator", 0, LF_FORBID_CLONE_OP }, { "forbid-switch-statement", LF_FORBID_SWITCH_STMT, 0 }, { "allow-switch-statement", 0, LF_FORBID_SWITCH_STMT }, { "forbid-implicit-type-methods", LF_FORBID_IMPLICIT_TYPE_METHODS, 0 }, { "allow-implicit-type-methods", 0, LF_FORBID_IMPLICIT_TYPE_METHODS }, { "forbid-auto-freeze", 0, LF_ALLOW_AUTO_FREEZE }, { "allow-auto-freeze", LF_ALLOW_AUTO_FREEZE, 0 }, { "forbid-compiler-internals", 0, LF_ALLOW_COMPILER_INTERNALS }, { "allow-compiler-internals", LF_ALLOW_COMPILER_INTERNALS, 0 }, }; Statement* SQParser::parseDirectiveStatement() { const char *sval = _lex._svalue; bool applyToDefault = false; if (strncmp(sval, "default:", 8) == 0) { applyToDefault = true; sval += 8; } const SQPragmaDescriptor *pragmaDesc = nullptr; for (const auto &desc : pragmaDescriptors) { if (strcmp(sval, desc.id) == 0) { pragmaDesc = &desc; break; } } if (pragmaDesc == nullptr) { reportDiagnostic(DiagnosticsId::DI_UNSUPPORTED_DIRECTIVE, sval); return nullptr; } SQInteger setFlags = pragmaDesc->setFlags, clearFlags = pragmaDesc->clearFlags; DirectiveStmt *d = newNode(_lex.tokenSpan()); d->applyToDefault = applyToDefault; d->setFlags = setFlags; d->clearFlags = clearFlags; _lang_features = (_lang_features | setFlags) & ~clearFlags; if (applyToDefault) _ss(_vm)->defaultLangFeatures = (_ss(_vm)->defaultLangFeatures | setFlags) & ~clearFlags; Lex(); return d; } void SQParser::checkBraceIndentationStyle() { if (_token == '{' && (_lex._prevflags & TF_PREP_EOL)) reportDiagnostic(DiagnosticsId::DI_EGYPT_BRACES); } void SQParser::Lex() { _token = _lex.Lex(); while (_token == TK_DIRECTIVE) { bool endOfLine = (_lex._prevtoken == '\n'); if (ProcessPosDirective()) { _token = _lex.Lex(); if (endOfLine) _lex._prevtoken = '\n'; } else break; } const bool forceIdentifier = (_token == TK_CLONE && (_lang_features & LF_FORBID_CLONE_OP)) || ((_token == TK_SWITCH || _token == TK_CASE || _token == TK_DEFAULT) && (_lang_features & LF_FORBID_SWITCH_STMT)); if (forceIdentifier) { _token = TK_IDENTIFIER; _lex.SetStringValue(); } } Expr* SQParser::Expect(SQInteger tok) { if(_token != tok) { if(_token == TK_CONSTRUCTOR && tok == TK_IDENTIFIER) { //do nothing } else { if(tok > 255) { const char *etypename; switch(tok) { case TK_IDENTIFIER: etypename = "IDENTIFIER"; break; case TK_STRING_LITERAL: etypename = "STRING_LITERAL"; break; case TK_INTEGER: etypename = "INTEGER"; break; case TK_FLOAT: etypename = "FLOAT"; break; default: etypename = _lex.Tok2Str(tok); } reportDiagnostic(DiagnosticsId::DI_EXPECTED_TOKEN, etypename); } else { char s[2] = {(char)tok, 0}; reportDiagnostic(DiagnosticsId::DI_EXPECTED_TOKEN, s); } } } Expr *ret = NULL; switch(tok) { case TK_IDENTIFIER: ret = newId(_lex._svalue); break; case TK_STRING_LITERAL: ret = newStringLiteral(_lex._svalue); break; case TK_INTEGER: ret = newNode(_lex.tokenSpan(), _lex._nvalue); break; case TK_FLOAT: ret = newNode(_lex.tokenSpan(), _lex._fvalue); break; } Lex(); return ret; } void SQParser::OptionalSemicolon() { if(_token == ';') { Lex(); return; } if(!IsEndOfStatement()) { reportDiagnostic(DiagnosticsId::DI_END_OF_STMT_EXPECTED); } } RootBlock* SQParser::parse() { try { Lex(); SourceLoc start = _lex.tokenStart(); RootBlock *rootBlock = newNode(arena(), start); while(_token > 0){ rootBlock->addStatement(parseStatement()); if(_lex._prevtoken != '}' && _lex._prevtoken != ';') OptionalSemicolon(); } rootBlock->setSpanEnd(_lex.currentPos()); return rootBlock; } catch (SQCompilation::CompilerError &) { return NULL; } } Block* SQParser::parseStatements(SourceLoc start) { NestingChecker nc(this); Block *result = newNode(arena(), start); while(_token != '}' && _token != TK_DEFAULT && _token != TK_CASE) { Statement *stmt = parseStatement(); result->addStatement(stmt); if(_lex._prevtoken != '}' && _lex._prevtoken != ';') OptionalSemicolon(); } result->setSpanEnd(_lex.currentPos()); return result; } void SQParser::onDocString(const char *doc_string) { if (_docObjectStack.back()->getDocString() != nullptr) reportDiagnostic(DiagnosticsId::DI_MULTIPLE_DOCSTRINGS); else { SQString *docStringCopy = SQString::Create(_ss(_vm), doc_string); if (docStringCopy) _docObjectStack.back()->setDocString(docStringCopy->_val); } } Statement* SQParser::parseStatement(bool closeframe) { NestingChecker nc(this); Statement *result = NULL; bool allowsImport = false; switch(_token) { case ';': allowsImport = true; result = newNode(_lex.tokenSpan()); Lex(); break; case TK_DOCSTRING: { SourceSpan span = _lex.tokenSpan(); onDocString(_lex._svalue); result = newNode(span); Lex(); break; } case TK_DIRECTIVE: allowsImport = true; result = parseDirectiveStatement(); break; case TK_IF: result = parseIfStatement(); break; case TK_WHILE: result = parseWhileStatement(); break; case TK_DO: result = parseDoWhileStatement(); break; case TK_FOR: result = parseForStatement(); break; case TK_FOREACH:result = parseForEachStatement(); break; case TK_SWITCH: result = parseSwitchStatement(); break; case TK_LOCAL: case TK_LET: result = parseLocalDeclStatement(); break; case TK_RETURN: case TK_YIELD: { bool isReturn = (_token == TK_RETURN); SourceSpan keywordSpan = _lex.tokenSpan(); Lex(); Expr *arg = NULL; if(!IsEndOfStatement()) { arg = Expression(SQE_RVALUE); } if (isReturn) { return newNode(keywordSpan, arg); } else { return newNode(keywordSpan, arg); } } case TK_BREAK: { SourceSpan span = _lex.tokenSpan(); Lex(); return newNode(span, nullptr); } case TK_CONTINUE: { SourceSpan span = _lex.tokenSpan(); Lex(); return newNode(span, nullptr); } case TK_FUNCTION: { SourceLoc funcStart = _lex.tokenStart(); result = parseLocalFunctionExprStmt(false, funcStart); break; } case TK_CLASS: { SourceLoc classStart = _lex.tokenStart(); result = parseLocalClassExprStmt(false, classStart); break; } case TK_ENUM: result = parseEnumStatement(false); break; case '{': { SQUnsignedInteger savedLangFeatures = _lang_features; SourceLoc blockStart = _lex.tokenStart(); Lex(); Block *block = parseStatements(blockStart); block->setSpanEnd(_lex.currentPos()); Expect('}'); _lang_features = savedLangFeatures; result = block; break; } case TK_TRY: result = parseTryCatchStatement(); break; case TK_THROW: { SourceSpan keywordSpan = _lex.tokenSpan(); Lex(); Expr *e = Expression(SQE_RVALUE); return newNode(keywordSpan, e); } case TK_CONST: result = parseConstStatement(false); break; case TK_GLOBAL: { SourceLoc globalStart = _lex.tokenStart(); Lex(); if (_token == TK_CONST) result = parseConstStatement(true, globalStart); else if (_token == TK_ENUM) result = parseEnumStatement(true, globalStart); else reportDiagnostic(DiagnosticsId::DI_GLOBAL_CONSTS_ONLY); break; } default: { // Check for contextual import keywords to switch parsing mode if (_are_imports_still_allowed && _token == TK_IDENTIFIER) { if (strcmp(_lex._svalue, "import") == 0 || strcmp(_lex._svalue, "from") == 0) { allowsImport = true; result = parseImportStatement(); break; } } Expr *e = Expression(SQE_REGULAR); return newNode(e); } } assert(result); if (!allowsImport) _are_imports_still_allowed = false; return result; } Expr* SQParser::parseCommaExpr(SQExpressionContext expression_context) { NestingChecker nc(this); Expr *expr = Expression(expression_context); if (_token == ',') { ArenaVector exprs(arena()); exprs.push_back(expr); while (_token == ',') { Lex(); exprs.push_back(Expression(expression_context)); } expr = newNode(arena(), std::move(exprs)); } return expr; } Expr* SQParser::Expression(SQExpressionContext expression_context) { NestingChecker nc(this); SQExpressionContext saved_expression_context = _expression_context; _expression_context = expression_context; Expr *expr = LogicalNullCoalesceExp(); switch(_token) { case '=': case TK_NEWSLOT: case TK_MINUSEQ: case TK_PLUSEQ: case TK_MULEQ: case TK_DIVEQ: case TK_MODEQ: { SQInteger op = _token; Lex(); Expr *e2 = Expression(SQE_RVALUE); switch (op) { case TK_NEWSLOT: expr = newNode(TO_NEWSLOT, expr, e2); break; case '=': //ASSIGN switch (expression_context) { case SQE_IF: reportDiagnostic(DiagnosticsId::DI_ASSIGN_INSIDE_FORBIDDEN, "if"); break; case SQE_LOOP_CONDITION: reportDiagnostic(DiagnosticsId::DI_ASSIGN_INSIDE_FORBIDDEN, "loop condition"); break; case SQE_SWITCH: reportDiagnostic(DiagnosticsId::DI_ASSIGN_INSIDE_FORBIDDEN, "switch"); break; case SQE_FUNCTION_ARG: reportDiagnostic(DiagnosticsId::DI_ASSIGN_INSIDE_FORBIDDEN, "function argument"); break; case SQE_RVALUE: reportDiagnostic(DiagnosticsId::DI_ASSIGN_INSIDE_FORBIDDEN, "expression"); break; case SQE_ARRAY_ELEM: reportDiagnostic(DiagnosticsId::DI_ASSIGN_INSIDE_FORBIDDEN, "array element"); break; case SQE_REGULAR: break; } expr = newNode(TO_ASSIGN, expr, e2); break; case TK_MINUSEQ: expr = newNode(TO_MINUSEQ, expr, e2); break; case TK_PLUSEQ: expr = newNode(TO_PLUSEQ, expr, e2); break; case TK_MULEQ: expr = newNode(TO_MULEQ, expr, e2); break; case TK_DIVEQ: expr = newNode(TO_DIVEQ, expr, e2); break; case TK_MODEQ: expr = newNode(TO_MODEQ, expr, e2); break; } } break; case '?': { Consume('?'); Expr *ifTrue = Expression(SQE_RVALUE); Expect(':'); Expr *ifFalse = Expression(SQE_RVALUE); expr = newNode(expr, ifTrue, ifFalse); } break; } _expression_context = saved_expression_context; return expr; } template Expr* SQParser::BIN_EXP(T f, enum TreeOp top, Expr *lhs) { SQInteger prevTok = _lex._prevtoken, tok = _token; unsigned prevFlags = _lex._prevflags; Lex(); checkSuspiciousUnaryOp(prevTok, tok, prevFlags); SQExpressionContext old = _expression_context; _expression_context = SQE_RVALUE; Expr *rhs = (this->*f)(); _expression_context = old; return newNode(top, lhs, rhs); } Expr* SQParser::LogicalNullCoalesceExp() { NestingChecker nc(this); Expr *lhs = LogicalOrExp(); for (;;) { nc.inc(); if (_token == TK_NULLCOALESCE) { Lex(); Expr *rhs = LogicalNullCoalesceExp(); lhs = newNode(TO_NULLC, lhs, rhs); } else return lhs; } } Expr* SQParser::LogicalOrExp() { NestingChecker nc(this); Expr *lhs = LogicalAndExp(); for (;;) { nc.inc(); if (_token == TK_OR) { Lex(); Expr *rhs = LogicalOrExp(); lhs = newNode(TO_OROR, lhs, rhs); } else return lhs; } } Expr* SQParser::LogicalAndExp() { NestingChecker nc(this); Expr *lhs = BitwiseOrExp(); for (;;) { nc.inc(); switch (_token) { case TK_AND: { Lex(); Expr *rhs = LogicalAndExp(); lhs = newNode(TO_ANDAND, lhs, rhs); } default: return lhs; } } } Expr* SQParser::BitwiseOrExp() { NestingChecker nc(this); Expr *lhs = BitwiseXorExp(); for (;;) { nc.inc(); if (_token == '|') { return BIN_EXP(&SQParser::BitwiseOrExp, TO_OR, lhs); } else return lhs; } } Expr* SQParser::BitwiseXorExp() { NestingChecker nc(this); Expr * lhs = BitwiseAndExp(); for (;;) { nc.inc(); if (_token == '^') { lhs = BIN_EXP(&SQParser::BitwiseAndExp, TO_XOR, lhs); } else return lhs; } } Expr* SQParser::BitwiseAndExp() { NestingChecker nc(this); Expr *lhs = EqExp(); for (;;) { nc.inc(); if (_token == '&') { lhs = BIN_EXP(&SQParser::EqExp, TO_AND, lhs); } else return lhs; } } Expr* SQParser::EqExp() { NestingChecker nc(this); Expr *lhs = CompExp(); for (;;) { nc.inc(); switch (_token) { case TK_EQ: lhs = BIN_EXP(&SQParser::CompExp, TO_EQ, lhs); break; case TK_NE: lhs = BIN_EXP(&SQParser::CompExp, TO_NE, lhs); break; case TK_3WAYSCMP: lhs = BIN_EXP(&SQParser::CompExp, TO_3CMP, lhs); break; default: return lhs; } } } Expr* SQParser::CompExp() { NestingChecker nc(this); Expr *lhs = ShiftExp(); for (;;) { nc.inc(); switch (_token) { case '>': lhs = BIN_EXP(&SQParser::ShiftExp, TO_GT, lhs); break; case '<': lhs = BIN_EXP(&SQParser::ShiftExp, TO_LT, lhs); break; case TK_GE: lhs = BIN_EXP(&SQParser::ShiftExp, TO_GE, lhs); break; case TK_LE: lhs = BIN_EXP(&SQParser::ShiftExp, TO_LE, lhs); break; case TK_IN: lhs = BIN_EXP(&SQParser::ShiftExp, TO_IN, lhs); break; case TK_INSTANCEOF: lhs = BIN_EXP(&SQParser::ShiftExp, TO_INSTANCEOF, lhs); break; case TK_NOT: { SourceLoc notStart = _lex.tokenStart(); Lex(); if (_token == TK_IN) { lhs = BIN_EXP(&SQParser::ShiftExp, TO_IN, lhs); lhs = newNode(TO_NOT, notStart, lhs); } else reportDiagnostic(DiagnosticsId::DI_EXPECTED_TOKEN, "in"); } default: return lhs; } } } Expr* SQParser::ShiftExp() { NestingChecker nc(this); Expr *lhs = PlusExp(); for (;;) { nc.inc(); switch (_token) { case TK_USHIFTR: lhs = BIN_EXP(&SQParser::PlusExp, TO_USHR, lhs); break; case TK_SHIFTL: lhs = BIN_EXP(&SQParser::PlusExp, TO_SHL, lhs); break; case TK_SHIFTR: lhs = BIN_EXP(&SQParser::PlusExp, TO_SHR, lhs); break; default: return lhs; } } } void SQParser::checkSuspiciousUnaryOp(SQInteger pprevTok, SQInteger tok, unsigned pprevFlags) { if (tok == '+' || tok == '-') { if (_expression_context == SQE_ARRAY_ELEM || _expression_context == SQE_FUNCTION_ARG) { if ((pprevFlags & (TF_PREP_EOL | TF_PREP_SPACE)) && (pprevTok != ',') && ((_lex._prevflags & TF_PREP_SPACE) == 0)) { reportDiagnostic(DiagnosticsId::DI_NOT_UNARY_OP, tok == '+' ? "+" : "-"); } } } } Expr* SQParser::PlusExp() { NestingChecker nc(this); Expr *lhs = MultExp(); for (;;) { nc.inc(); switch (_token) { case '+': lhs = BIN_EXP(&SQParser::MultExp, TO_ADD, lhs); break; case '-': lhs = BIN_EXP(&SQParser::MultExp, TO_SUB, lhs); break; default: return lhs; } } } Expr* SQParser::MultExp() { NestingChecker nc(this); Expr *lhs = PrefixedExpr(); for (;;) { nc.inc(); switch (_token) { case '*': lhs = BIN_EXP(&SQParser::PrefixedExpr, TO_MUL, lhs); break; case '/': lhs = BIN_EXP(&SQParser::PrefixedExpr, TO_DIV, lhs); break; case '%': lhs = BIN_EXP(&SQParser::PrefixedExpr, TO_MOD, lhs); break; default: return lhs; } } } void SQParser::checkSuspiciousBracket() { assert(_token == '(' || _token == '['); if (_expression_context == SQE_ARRAY_ELEM || _expression_context == SQE_FUNCTION_ARG) { if (_lex._prevtoken != ',') { if (_lex._prevflags & (TF_PREP_EOL | TF_PREP_SPACE)) { char op[] = { (char)_token, '\0' }; reportDiagnostic(DiagnosticsId::DI_SUSPICIOUS_BRACKET, op, _token == '(' ? "function call" : "access to member"); } } } } static const char *opname(SQInteger op) { switch (op) { case '.': return "."; case TK_NULLGETSTR: return "?."; case TK_TYPE_METHOD_GETSTR: return ".$"; case TK_NULLABLE_TYPE_METHOD_GETSTR: return "?.$"; default: return ""; } } Expr* SQParser::PrefixedExpr() { NestingChecker nc(this); //if 'pos' != -1 the previous variable is a local variable SQInteger pos; Expr *e = Factor(pos); bool nextIsNullable = false; for(;;) { nc.inc(); switch(_token) { case '.': case TK_NULLGETSTR: case TK_TYPE_METHOD_GETSTR: case TK_NULLABLE_TYPE_METHOD_GETSTR: { if (_token == TK_NULLGETSTR || _token == TK_NULLABLE_TYPE_METHOD_GETSTR || nextIsNullable) { nextIsNullable = true; } bool isTypeMethod = _token == TK_TYPE_METHOD_GETSTR || _token == TK_NULLABLE_TYPE_METHOD_GETSTR; SQInteger tok = _token; Lex(); if ((_lex._prevflags & (TF_PREP_SPACE | TF_PREP_EOL)) != 0) { reportDiagnostic(DiagnosticsId::DI_SPACE_SEP_FIELD_NAME, opname(tok)); } Expr *receiver = e; Id *id = (Id *)Expect(TK_IDENTIFIER); assert(id); // Use end from Id's span (after the identifier) e = newNode(receiver, id->name(), nextIsNullable, isTypeMethod, id->sourceSpan().end); //-V522 break; } case '[': case TK_NULLGETOBJ: { if (_token == TK_NULLGETOBJ || nextIsNullable) { nextIsNullable = true; } if(_lex._prevtoken == '\n') reportDiagnostic(DiagnosticsId::DI_BROKEN_SLOT_DECLARATION); if (_token == '[') checkSuspiciousBracket(); Lex(); Expr *receiver = e; Expr *key = Expression(SQE_RVALUE); SourceLoc end = _lex.currentPos(); Expect(']'); e = newNode(receiver, key, nextIsNullable, end); break; } case TK_MINUSMINUS: case TK_PLUSPLUS: { nextIsNullable = false; if(IsEndOfStatement()) return e; SQInteger diff = (_token==TK_MINUSMINUS) ? -1 : 1; SourceLoc opEnd = _lex.currentPos(); Lex(); e = newNode(e, diff, IF_POSTFIX, opEnd); } return e; case '(': case TK_NULLCALL: { if (_lex._prevflags & TF_PREP_EOL && _token == '(') { reportDiagnostic(DiagnosticsId::DI_PAREN_IS_FUNC_CALL); } SQInteger nullcall = (_token==TK_NULLCALL || nextIsNullable); nextIsNullable = !!nullcall; CallExpr *call = newNode(arena(), e, nullcall); if (_token == '(') checkSuspiciousBracket(); Lex(); while (_token != ')') { call->addArgument(Expression(SQE_FUNCTION_ARG)); if (_token == ',') { Lex(); } } call->setSpanEnd(_lex.currentPos()); Lex(); e = call; break; } default: return e; } } } static void appendStringData(sqvector &dst, const char *b) { while (*b) { dst.push_back(*b++); } } Expr *SQParser::parseStringTemplate() { // '$' TK_TEMPLATE_PREFIX? (arg TK_TEMPLATE_INFIX)* arg? TK_TEMPLATE_SUFFX _lex._state = LS_TEMPLATE; _lex._expectedToken = TK_TEMPLATE_PREFIX; Lex(); int idx = 0; sqvector formatString(_ctx.allocContext()); sqvector args(_ctx.allocContext()); char buffer[64] = {0}; SourceLoc fmtStart = _lex.tokenStart(); SQInteger tok = -1; while ((tok = _token) != SQUIRREL_EOB) { if (tok != TK_TEMPLATE_PREFIX && tok != TK_TEMPLATE_INFIX && tok != TK_TEMPLATE_SUFFIX) { reportDiagnostic(DiagnosticsId::DI_EXPECTED_TOKEN, "STRING_LITERAL"); return nullptr; } appendStringData(formatString, _lex._svalue); if (tok != TK_TEMPLATE_SUFFIX) { snprintf(buffer, sizeof buffer, "%d", idx++); appendStringData(formatString, buffer); _lex._expectedToken = -1; _lex._state = LS_REGULAR; Lex(); Expr *arg = Expression(SQE_FUNCTION_ARG); args.push_back(arg); if (_token != '}') { reportDiagnostic(DiagnosticsId::DI_EXPECTED_TOKEN, "}"); return nullptr; } formatString.push_back('}'); _lex._state = LS_TEMPLATE; _lex._expectedToken = TK_TEMPLATE_INFIX; } else { break; } Lex(); } formatString.push_back('\0'); SourceLoc templateEnd = _lex.currentPos(); Expr *result = nullptr; SourceSpan fmtSpan = {fmtStart, templateEnd}; LiteralExpr *fmt = newNode(fmtSpan, copyString(&formatString[0])); if (args.empty()) { result = fmt; } else { // GetFieldExpr: from fmt start to current position (synthetic .subst access) Expr *callee = newNode(fmt, "subst", false, /* force type method */ true, templateEnd); // CallExpr: from callee start to end of arguments (which is templateEnd for string templates) CallExpr *call = newNode(arena(), callee, false, templateEnd); for (Expr *arg : args) call->addArgument(arg); result = call; } _lex._expectedToken = -1; _lex._state = LS_REGULAR; Lex(); return result; } Expr* SQParser::Factor(SQInteger &pos) { NestingChecker nc(this); Expr *r = NULL; switch(_token) { case TK_STRING_LITERAL: r = newStringLiteral(_lex._svalue); Lex(); break; case TK_TEMPLATE_OP: r = parseStringTemplate(); break; case TK_BASE: { SourceSpan span = _lex.tokenSpan(); Lex(); r = newNode(span); break; } case TK_IDENTIFIER: r = newId(_lex._svalue); Lex(); break; case TK_CONSTRUCTOR: { SourceSpan span = _lex.tokenSpan(); Lex(); r = newNode(span, "constructor"); break; } case TK_THIS: { SourceSpan span = _lex.tokenSpan(); Lex(); r = newNode(span, "this"); break; } case TK_DOUBLE_COLON: { // "::" if (_lang_features & LF_FORBID_ROOT_TABLE) reportDiagnostic(DiagnosticsId::DI_ROOT_TABLE_FORBIDDEN); SourceSpan span = _lex.tokenSpan(); _token = '.'; /* hack: drop into PrefixExpr, case '.'*/ r = newNode(span); break; } case TK_NULL: { SourceSpan span = _lex.tokenSpan(); Lex(); r = newNode(span); break; } case TK_INTEGER: { SourceSpan span = _lex.tokenSpan(); SQInteger val = _lex._nvalue; Lex(); r = newNode(span, val); break; } case TK_FLOAT: { SourceSpan span = _lex.tokenSpan(); SQFloat val = _lex._fvalue; Lex(); r = newNode(span, val); break; } case TK_TRUE: case TK_FALSE: { SourceSpan span = _lex.tokenSpan(); bool val = (_token == TK_TRUE); Lex(); r = newNode(span, val); break; } case '[': { SourceLoc start = _lex.tokenStart(); Lex(); ArrayExpr *arr = newNode(arena(), start); bool commaSeparated = false; bool spaceSeparated = false; bool reported = false; while(_token != ']') { Expr *v = Expression(SQE_ARRAY_ELEM); arr->addValue(v); if (_token == ',') { commaSeparated = true; } else if (_token != ']') { spaceSeparated = true; } if (commaSeparated && spaceSeparated && !reported) { reported = true; // do not spam in output, a single diag seems to be enough reportDiagnostic(DiagnosticsId::DI_MIXED_SEPARATORS, "elements of array"); } if (_token == ',') { Lex(); } } arr->setSpanEnd(_lex.currentPos()); Lex(); r = arr; } break; case '{': { SourceLoc start = _lex.tokenStart(); Lex(); TableExpr *t = newNode(arena(), start); _docObjectStack.push_back(&t->docObject); ParseTableOrClass(t, ',', '}'); _docObjectStack.pop_back(); // TableExpr end is set by ParseTableOrClass via Lex() after '}' r = t; break; } case TK_FUNCTION: r = FunctionExp(false); break; case '@': r = FunctionExp(true); break; case TK_CLASS: { SourceLoc classStart = _lex.tokenStart(); Lex(); r = ClassExp(classStart, NULL); break; } case '-': { SourceLoc opStart = _lex.tokenStart(); Lex(); switch(_token) { case TK_INTEGER: { SourceSpan span = {opStart, _lex.currentPos()}; SQInteger val = -_lex._nvalue; Lex(); r = newNode(span, val); break; } case TK_FLOAT: { SourceSpan span = {opStart, _lex.currentPos()}; SQFloat val = -_lex._fvalue; Lex(); r = newNode(span, val); break; } default: r = newNode(TO_NEG, opStart, PrefixedExpr()); break; } break; } case '!': { SourceLoc opStart = _lex.tokenStart(); Lex(); r = newNode(TO_NOT, opStart, PrefixedExpr()); break; } case '~': { SourceLoc opStart = _lex.tokenStart(); Lex(); if(_token == TK_INTEGER) { SourceSpan span = {opStart, _lex.currentPos()}; SQInteger val = ~_lex._nvalue; Lex(); r = newNode(span, val); } else { r = newNode(TO_BNOT, opStart, PrefixedExpr()); } break; } case TK_TYPEOF: { SourceLoc opStart = _lex.tokenStart(); Lex(); r = newNode(TO_TYPEOF, opStart, PrefixedExpr()); break; } case TK_RESUME: { SourceLoc opStart = _lex.tokenStart(); Lex(); r = newNode(TO_RESUME, opStart, PrefixedExpr()); break; } case TK_CLONE: { SourceLoc opStart = _lex.tokenStart(); Lex(); r = newNode(TO_CLONE, opStart, PrefixedExpr()); break; } case TK_STATIC: { SourceLoc opStart = _lex.tokenStart(); Lex(); r = newNode(TO_STATIC_MEMO, opStart, PrefixedExpr()); break; } case TK_CONST: { SourceLoc opStart = _lex.tokenStart(); Lex(); r = newNode(TO_INLINE_CONST, opStart, PrefixedExpr()); break; } case TK_MINUSMINUS : case TK_PLUSPLUS : r = PrefixIncDec(_token); break; case TK_DELETE : if (_lang_features & LF_FORBID_DELETE_OP) { reportDiagnostic(DiagnosticsId::DI_DELETE_OP_FORBIDDEN); } r = DeleteExpr(); break; case '(': { SourceLoc start = _lex.tokenStart(); Lex(); Expr *inner = Expression(_expression_context); Expect(')'); r = newNode(TO_PAREN, start, inner); break; } case TK_CODE_BLOCK_EXPR: { if ((_lang_features & LF_ALLOW_COMPILER_INTERNALS) == 0) reportDiagnostic(DiagnosticsId::DI_COMPILER_INTERNALS_FORBIDDEN); SQExpressionContext saved_expression_context = _expression_context; _expression_context = SQE_REGULAR; SQUnsignedInteger savedLangFeatures = _lang_features; SourceLoc start = _lex.tokenStart(); Lex(); Block *blk = parseStatements(start); blk->setIsExprBlock(); blk->setSpanEnd(_lex.currentPos()); r = newNode(blk); Expect('}'); _lang_features = savedLangFeatures; _expression_context = saved_expression_context; break; } case TK___LINE__: { SourceSpan span = _lex.tokenSpan(); SQInteger val = _lex._currentline; Lex(); r = newNode(span, val); break; } case TK___FILE__: { SourceSpan span = _lex.tokenSpan(); Lex(); r = newNode(span, _sourcename); break; } default: reportDiagnostic(DiagnosticsId::DI_EXPECTED_TOKEN, "expression"); } return r; } void SQParser::ParseTableOrClass(TableExpr *decl, SQInteger separator, SQInteger terminator) { NestingChecker nc(this); NewObjectType otype = separator==',' ? NEWOBJ_TABLE : NEWOBJ_CLASS; while(_token != terminator) { SourceSpan keySpan = _lex.tokenSpan(); unsigned flags = 0; //check if is an static if(otype == NEWOBJ_CLASS) { if(_token == TK_STATIC) { flags |= TMF_STATIC; Lex(); keySpan = _lex.tokenSpan(); } } switch (_token) { case TK_DOCSTRING: { onDocString(_lex._svalue); Lex(); break; } case TK_FUNCTION: case TK_CONSTRUCTOR: { SQInteger tk = _token; SourceLoc funcStart = _lex.tokenStart(); Lex(); FuncAttrFlagsType attr = ParseFunctionAttributes(); Id *funcName = tk == TK_FUNCTION ? (Id *)Expect(TK_IDENTIFIER) : newNode(_lex.tokenSpan(), "constructor"); assert(funcName); LiteralExpr *key = newNode(funcName->sourceSpan(), funcName->name()); //-V522 Expect('('); FunctionExpr *f = CreateFunction(funcStart, funcName, false, tk == TK_CONSTRUCTOR); f->setPure(attr & FATTR_PURE); f->setNodiscard(attr & FATTR_NODISCARD); decl->addMember(key, f, flags); } break; case '[': { Lex(); Expr *key = Expression(SQE_RVALUE); //-V522 assert(key); Expect(']'); Expect('='); Expr *value = Expression(SQE_RVALUE); decl->addMember(key, value, flags | TMF_DYNAMIC_KEY); break; } case TK_STRING_LITERAL: //JSON if (otype == NEWOBJ_TABLE) { //only works for tables LiteralExpr *key = (LiteralExpr *)Expect(TK_STRING_LITERAL); //-V522 assert(key); Expect(':'); Expr *expr = Expression(SQE_RVALUE); decl->addMember(key, expr, flags | TMF_JSON); break; } //-V796 default: { Id *id = (Id *)Expect(TK_IDENTIFIER); assert(id); LiteralExpr *key = newNode(keySpan, id->name()); //-V522 if ((otype == NEWOBJ_TABLE) && (_token == TK_IDENTIFIER || _token == separator || _token == terminator || _token == '[' || _token == TK_FUNCTION)) { decl->addMember(key, id, flags); } else { Expect('='); Expr *expr = Expression(SQE_RVALUE); decl->addMember(key, expr, flags); } } } if (_token == separator) Lex(); //optional comma/semicolon } decl->setSpanEnd(_lex.currentPos()); Lex(); } Decl *SQParser::parseLocalFunctionExprStmt(bool assignable, SourceLoc keywordStart) { SourceLoc funcStart = _lex.tokenStart(); assert(_token == TK_FUNCTION); Lex(); FuncAttrFlagsType attr = ParseFunctionAttributes(); Id *varname = (Id *)Expect(TK_IDENTIFIER); Expect('('); FunctionExpr *f = CreateFunction(funcStart, varname, false); f->setPure(attr & FATTR_PURE); f->setNodiscard(attr & FATTR_NODISCARD); VarDecl *d = newNode(keywordStart, varname, f, assignable); //-V522 return d; } Decl *SQParser::parseLocalClassExprStmt(bool assignable, SourceLoc keywordStart) { SourceLoc classStart = _lex.tokenStart(); assert(_token == TK_CLASS); Lex(); Id *varname = (Id *)Expect(TK_IDENTIFIER); ClassExpr *cls = ClassExp(classStart, NULL); VarDecl *d = newNode(keywordStart, varname, cls, assignable); //-V522 return d; } Decl* SQParser::parseLocalDeclStatement(bool onlySingleVariable) { NestingChecker nc(this); assert(_token == TK_LET || _token == TK_LOCAL); SourceLoc keywordStart = _lex.tokenStart(); bool assignable = _token == TK_LOCAL; Lex(); if (_token == TK_FUNCTION) { return parseLocalFunctionExprStmt(assignable, keywordStart); } else if (_token == TK_CLASS) { return parseLocalClassExprStmt(assignable, keywordStart); } DeclGroup *decls = NULL; DestructuringDecl *dd = NULL; Decl *decl = NULL; SQInteger destructurer = 0; if (_token == '{' || _token == '[') { if (onlySingleVariable) reportDiagnostic(DiagnosticsId::DI_ONLY_SINGLE_VARIABLE_DECLARATION); destructurer = _token; Lex(); // Use keywordStart (position of 'local'/'let') so span includes the keyword decls = dd = newNode(arena(), keywordStart, destructurer == '{' ? DT_TABLE : DT_ARRAY); } do { Id *varname = (Id *)Expect(TK_IDENTIFIER); assert(varname); VarDecl *cur = NULL; unsigned typeMask = _token == ':' ? parseTypeMask(destructurer == 0) : ~0u; // Determine start position: // - For destructured vars, use varname's span start (keyword covered by DestructuringDecl) // - For first non-destructured var, use keywordStart (to include 'local'/'let') // - For subsequent vars in a group, use varname's span start (group covers keyword) bool isFirstNonDestructured = (!destructurer && decl == NULL && decls == NULL); SourceLoc varStart = isFirstNonDestructured ? keywordStart : varname->sourceSpan().start; //-V522 if(_token == '=') { Lex(); Expr *expr = Expression(SQE_REGULAR); if (!assignable && expr->op() == TO_FUNCTION) { FunctionExpr *f = expr->asFunctionExpr(); if (!f->name() || f->name()[0] == '(') { f->setName(varname->name()); } } cur = newNode(varStart, varname, expr, assignable, destructurer != 0); //-V522 } else { if (!assignable && !destructurer) _ctx.reportDiagnostic(DiagnosticsId::DI_UNINITIALIZED_BINDING, varname->lineStart(), varname->columnStart(), varname->textWidth(), varname->name()); //-V522 cur = newNode(varStart, varname, nullptr, assignable, destructurer != 0); } cur->setTypeMask(typeMask); if (decls) { decls->addDeclaration(cur); } else if (decl) { decls = newNode(arena(), keywordStart); decls->addDeclaration(static_cast(decl)); decls->addDeclaration(cur); decl = decls; } else { decl = cur; } if (destructurer) { if (_token == ',') { Lex(); if (_token == ']' || _token == '}') break; } else if (_token == TK_IDENTIFIER) continue; else break; } else { if (_token == ',') { if (onlySingleVariable) reportDiagnostic(DiagnosticsId::DI_ONLY_SINGLE_VARIABLE_DECLARATION); Lex(); } else break; } } while(1); if (destructurer) { Expect(destructurer=='[' ? ']' : '}'); Expect('='); assert(dd); dd->setExpression(Expression(SQE_RVALUE)); //-V522 return dd; } else { return decls ? static_cast(decls) : decl; } } Statement* SQParser::IfLikeBlock(bool &wrapped) { NestingChecker nc(this); Statement *stmt = NULL; if (_token == '{') { wrapped = false; SourceLoc blockStart = _lex.tokenStart(); Lex(); Block *block = parseStatements(blockStart); block->setSpanEnd(_lex.currentPos()); Expect('}'); stmt = block; } else { wrapped = true; stmt = parseStatement(); SourceSpan stmtSpan = stmt->sourceSpan(); //-V522 Block *block = newNode(arena(), stmtSpan.start); block->addStatement(stmt); block->setSpanEnd(stmtSpan.end); stmt = block; if (_lex._prevtoken != '}' && _lex._prevtoken != ';') OptionalSemicolon(); } return stmt; } Statement * SQParser::parseIfStatement() { NestingChecker nc(this); SourceLoc start = _lex.tokenStart(); SQInteger prevTok = _lex._prevtoken; Consume(TK_IF); Statement *initializer = NULL; Expr *cond = NULL; Expect('('); if (_token == TK_LOCAL || _token == TK_LET) { initializer = parseLocalDeclStatement(true); assert(initializer); if (initializer->asDeclaration()->asVarDecl()->initializer() == NULL) //-V522 reportDiagnostic(DiagnosticsId::DI_INITIALIZATION_REQUIRED); if (_token == ';') { Lex(); cond = Expression(SQE_IF); } else { Id* nameId = initializer->asDeclaration()->asVarDecl()->nameId(); //-V522 cond = newNode(nameId->sourceSpan(), nameId->name()); } } else { cond = Expression(SQE_IF); } Expect(')'); checkBraceIndentationStyle(); bool wrapped = false; Statement *thenB = IfLikeBlock(wrapped); Statement *elseB = NULL; if (_token == TK_ELSE) { SourceSpan elseSpan = _lex.tokenSpan(); Lex(); if (_token != '{' && prevTok != TK_ELSE && wrapped && start.line != elseSpan.start.line && start.column != elseSpan.start.column) { _ctx.reportDiagnostic(DiagnosticsId::DI_SUSPICIOUS_FMT, elseSpan.start.line, elseSpan.start.column, elseSpan.textWidth()); } checkBraceIndentationStyle(); elseB = IfLikeBlock(wrapped); if (!IsEndOfStatement()) { reportDiagnostic(DiagnosticsId::DI_STMT_SAME_LINE, "else"); } } else if (!IsEndOfStatement()) { reportDiagnostic(DiagnosticsId::DI_STMT_SAME_LINE, "then"); } if (_token != SQUIRREL_EOB && _token != '}' && _token != ']' && _token != ')' && _token != ',') { int32_t lastLine = elseB ? elseB->lineEnd() : thenB->lineEnd(); SourceLoc curTok = _lex.tokenStart(); if (curTok.line != lastLine && curTok.column > start.column) { reportDiagnostic(DiagnosticsId::DI_SUSPICIOUS_FMT); } } if (initializer) { // wrap initializer and condition in a {} block so they are properly scoped Block *block = newNode(arena(), start); block->addStatement(initializer); block->addStatement(newNode(start, cond, thenB, elseB)); block->setSpanEnd((elseB ? elseB : thenB)->sourceSpan().end); return block; } else { return newNode(start, cond, thenB, elseB); } } WhileStatement* SQParser::parseWhileStatement() { NestingChecker nc(this); SourceLoc start = _lex.tokenStart(); Consume(TK_WHILE); Expect('('); Expr *cond = Expression(SQE_LOOP_CONDITION); Expect(')'); bool wrapped = false; checkBraceIndentationStyle(); Statement *body = IfLikeBlock(wrapped); if (!IsEndOfStatement()) { reportDiagnostic(DiagnosticsId::DI_STMT_SAME_LINE, "while loop body"); } if (_token != SQUIRREL_EOB && _token != '}') { SourceLoc curTok = _lex.tokenStart(); if (curTok.line != body->lineEnd() && curTok.column > start.column) { reportDiagnostic(DiagnosticsId::DI_SUSPICIOUS_FMT); } } return newNode(start, cond, body); } DoWhileStatement* SQParser::parseDoWhileStatement() { NestingChecker nc(this); SourceLoc start = _lex.tokenStart(); Consume(TK_DO); // DO bool wrapped = false; checkBraceIndentationStyle(); Statement *body = IfLikeBlock(wrapped); Expect(TK_WHILE); Expect('('); Expr *cond = Expression(SQE_LOOP_CONDITION); SourceLoc end = _lex.currentPos(); Expect(')'); return newNode(start, body, cond, end); } ForStatement* SQParser::parseForStatement() { NestingChecker nc(this); SourceLoc start = _lex.tokenStart(); Consume(TK_FOR); Expect('('); Node *init = NULL; if (_token == TK_LOCAL) init = parseLocalDeclStatement(); else if (_token != ';') { init = parseCommaExpr(SQE_REGULAR); } Expect(';'); Expr *cond = NULL; if(_token != ';') { cond = Expression(SQE_LOOP_CONDITION); } Expect(';'); Expr *mod = NULL; if(_token != ')') { mod = parseCommaExpr(SQE_REGULAR); } Expect(')'); bool wrapped = false; checkBraceIndentationStyle(); Statement *body = IfLikeBlock(wrapped); if (!IsEndOfStatement()) { reportDiagnostic(DiagnosticsId::DI_STMT_SAME_LINE, "for loop body"); } if (_token != SQUIRREL_EOB && _token != '}') { SourceLoc curTok = _lex.tokenStart(); if (curTok.line != body->lineEnd() && curTok.column > start.column) { reportDiagnostic(DiagnosticsId::DI_SUSPICIOUS_FMT); } } return newNode(start, init, cond, mod, body); } void SQParser::parseDestructuringFields(DestructuringDecl *destr) { while (_token != '}' && _token != ']' && _token != SQUIRREL_EOB) { Id *fieldname = (Id *)Expect(TK_IDENTIFIER); assert(fieldname); if (!fieldname) break; unsigned typeMask = _token == ':' ? parseTypeMask(false) : ~0u; SourceLoc varStart = fieldname->sourceSpan().start; Expr *defaultExpr = nullptr; if (_token == '=') { Lex(); defaultExpr = Expression(SQE_REGULAR); } VarDecl *fieldDecl = newNode(varStart, fieldname, defaultExpr, /*assignable*/false, /*destructured*/true); fieldDecl->setTypeMask(typeMask); destr->addDeclaration(fieldDecl); if (_token == ',') { Lex(); if (_token == ']' || _token == '}') break; } else if (_token == TK_IDENTIFIER) continue; else break; } } ForeachStatement* SQParser::parseForEachStatement() { NestingChecker nc(this); SourceLoc start = _lex.tokenStart(); Consume(TK_FOREACH); Expect('('); Id *idxname = NULL; Id *valname = NULL; DestructuringDecl *valDestr = NULL; auto parseValDestructuring = [this, &valDestr]() -> Id* { SQInteger destructurer = _token; SourceLoc keywordStart = _lex.tokenStart(); char buf[32]; snprintf(buf, sizeof(buf), "@FE_VAL%u", _foreachDestrCounter++); Id *surrogateName = newId(buf); Lex(); // consume '{' or '[' valDestr = newNode(arena(), keywordStart, destructurer == '{' ? DT_TABLE : DT_ARRAY); parseDestructuringFields(valDestr); Expect(destructurer == '{' ? '}' : ']'); valDestr->setExpression(surrogateName); return surrogateName; }; if (_token == '{' || _token == '[') { valname = parseValDestructuring(); } else { valname = (Id *)Expect(TK_IDENTIFIER); assert(valname); if (_token == ',') { idxname = valname; Lex(); if (_token == '{' || _token == '[') { valname = parseValDestructuring(); } else { valname = (Id *)Expect(TK_IDENTIFIER); assert(valname); if (strcmp(idxname->name(), valname->name()) == 0) //-V522 _ctx.reportDiagnostic(DiagnosticsId::DI_SAME_FOREACH_KV_NAMES, valname->lineStart(), valname->columnStart(), valname->textWidth(), valname->name()); } } } Expect(TK_IN); Expr *contnr = Expression(SQE_RVALUE); Expect(')'); bool wrapped = false; checkBraceIndentationStyle(); Statement *body = IfLikeBlock(wrapped); if (!IsEndOfStatement()) { reportDiagnostic(DiagnosticsId::DI_STMT_SAME_LINE, "foreach loop body"); } if (_token != SQUIRREL_EOB && _token != '}') { SourceLoc curTok = _lex.tokenStart(); if (curTok.line != body->lineEnd() && curTok.column > start.column) { reportDiagnostic(DiagnosticsId::DI_SUSPICIOUS_FMT); } } if (valDestr) { Block *wrap = newNode(arena(), body->sourceSpan().start); wrap->addStatement(valDestr); wrap->addStatement(body); wrap->setSpanEnd(body->sourceSpan().end); body = wrap; } VarDecl *idxDecl = idxname ? newNode(idxname->sourceSpan().start, idxname, nullptr, false) : NULL; VarDecl *valDecl = valname ? newNode(valname->sourceSpan().start, valname, nullptr, false) : NULL; return newNode(start, idxDecl, valDecl, contnr, body); } SwitchStatement* SQParser::parseSwitchStatement() { NestingChecker nc(this); SourceLoc start = _lex.tokenStart(); Consume(TK_SWITCH); Expect('('); Expr *switchExpr = Expression(SQE_SWITCH); Expect(')'); checkBraceIndentationStyle(); Expect('{'); SwitchStatement *switchStmt = newNode(start, arena(), switchExpr); while(_token == TK_CASE) { Consume(TK_CASE); Expr *cond = Expression(SQE_RVALUE); Expect(':'); SourceLoc caseBodyStart = _lex.currentPos(); checkBraceIndentationStyle(); Statement *caseBody = parseStatements(caseBodyStart); switchStmt->addCases(cond, caseBody); } if(_token == TK_DEFAULT) { Consume(TK_DEFAULT); Expect(':'); SourceLoc defaultBodyStart = _lex.currentPos(); checkBraceIndentationStyle(); switchStmt->addDefault(parseStatements(defaultBodyStart)); } switchStmt->setSpanEnd(_lex.currentPos()); Expect('}'); return switchStmt; } LiteralExpr* SQParser::ExpectScalar() { NestingChecker nc(this); LiteralExpr *ret = NULL; SourceLoc start = _lex.tokenStart(); switch(_token) { case TK_NULL: ret = newNode(_lex.tokenSpan()); break; case TK_INTEGER: ret = newNode(_lex.tokenSpan(), _lex._nvalue); break; case TK_FLOAT: ret = newNode(_lex.tokenSpan(), _lex._fvalue); break; case TK_STRING_LITERAL: ret = newStringLiteral(_lex._svalue); break; case TK_TRUE: case TK_FALSE: ret = newNode(_lex.tokenSpan(), (bool)(_token == TK_TRUE ? 1 : 0)); break; case '-': { Lex(); SourceSpan span = {start, _lex.currentPos()}; switch(_token) { case TK_INTEGER: ret = newNode(span, -_lex._nvalue); break; case TK_FLOAT: ret = newNode(span, -_lex._fvalue); break; default: reportDiagnostic(DiagnosticsId::DI_SCALAR_EXPECTED, "integer, float"); } break; } default: reportDiagnostic(DiagnosticsId::DI_SCALAR_EXPECTED, "integer, float, or string"); } Lex(); return ret; } ConstDecl* SQParser::parseConstFunctionExprStmt(bool global, SourceLoc globalStart) { SourceLoc start = globalStart.isValid() ? globalStart : _lex.tokenStart(); assert(_token == TK_FUNCTION); Lex(); FuncAttrFlagsType attr = ParseFunctionAttributes(); Id *funcName = (Id *)Expect(TK_IDENTIFIER); Expect('('); FunctionExpr *f = CreateFunction(start, funcName, false); f->setPure(attr & FATTR_PURE); f->setNodiscard(attr & FATTR_NODISCARD); // Wrap function in const inline operator Expr *constFunc = newNode(TO_INLINE_CONST, start, f); ConstDecl *d = newNode(start, funcName->name(), constFunc, global); //-V522 return d; } ConstDecl* SQParser::parseConstStatement(bool global, SourceLoc globalStart) { NestingChecker nc(this); SourceLoc start = globalStart.isValid() ? globalStart : _lex.tokenStart(); Lex(); if (_token == TK_FUNCTION) { return parseConstFunctionExprStmt(global, globalStart); } Id *id = (Id *)Expect(TK_IDENTIFIER); Expect('='); Expr *valExpr = Expression(SQE_RVALUE); ConstDecl *d = newNode(start, id->name(), valExpr, global); //-V522 OptionalSemicolon(); return d; } EnumDecl* SQParser::parseEnumStatement(bool global, SourceLoc globalStart) { NestingChecker nc(this); SourceLoc start = globalStart.isValid() ? globalStart : _lex.tokenStart(); Lex(); Id *id = (Id *)Expect(TK_IDENTIFIER); EnumDecl *decl = newNode(arena(), start, id->name(), global); //-V522 checkBraceIndentationStyle(); Expect('{'); SQInteger nval = 0; while(_token != '}') { Id *key = (Id *)Expect(TK_IDENTIFIER); const char* keyName = key->name(); //-V522 for (EnumConst& ec : decl->consts()) if (*keyName == *ec.id && !strcmp(keyName, ec.id)) reportDiagnostic(DiagnosticsId::DI_DUPLICATE_KEY, keyName); LiteralExpr *valExpr = NULL; if(_token == '=') { // TODO1: should it behave like C does? // TODO2: should float and string literal be allowed here? Lex(); valExpr = ExpectScalar(); } else { valExpr = newNode(key->sourceSpan(), nval++); } decl->addConst(keyName, valExpr); //-V522 if(_token == ',') Lex(); } decl->setSpanEnd(_lex.currentPos()); Lex(); return decl; } TryStatement* SQParser::parseTryCatchStatement() { NestingChecker nc(this); SourceLoc start = _lex.tokenStart(); Consume(TK_TRY); checkBraceIndentationStyle(); Statement *t = parseStatement(); Expect(TK_CATCH); Expect('('); Id *exid = (Id *)Expect(TK_IDENTIFIER); Expect(')'); checkBraceIndentationStyle(); Statement *cth = parseStatement(); return newNode(start, t, exid, cth); } Id* SQParser::generateSurrogateFunctionName() { const char * fileName = _sourcename ? _sourcename : "unknown"; int lineNum = int(_lex._currentline); const char * rightSlash = std::max(strrchr(fileName, '/'), strrchr(fileName, '\\')); constexpr int maxLen = 256; char buf[maxLen]; scsprintf(buf, maxLen, "(%s:%d)", rightSlash ? (rightSlash + 1) : fileName, lineNum); return newId(buf); } SQParser::FuncAttrFlagsType SQParser::ParseFunctionAttributes() { if (_token != '[') return 0; Lex(); FuncAttrFlagsType attrVal = 0; while (_token != ']') { if (_token == TK_IDENTIFIER) { FuncAttrFlagsType flag = 0; if (strcmp(_lex._svalue, "pure")==0) { flag = FATTR_PURE; } else if (strcmp(_lex._svalue, "nodiscard")==0) { flag = FATTR_NODISCARD; } else { reportDiagnostic(DiagnosticsId::DI_EXPECTED_TOKEN, "valid attribute name"); } if (attrVal & flag) { reportDiagnostic(DiagnosticsId::DI_DUPLICATE_FUNC_ATTR, _lex._svalue); } attrVal |= flag; } else { reportDiagnostic(DiagnosticsId::DI_EXPECTED_TOKEN, "attribute name"); } Lex(); if (_token == ',') { Lex(); } } Expect(']'); return attrVal; } FunctionExpr* SQParser::FunctionExp(bool lambda) { NestingChecker nc(this); SourceLoc start = _lex.tokenStart(); Lex(); FuncAttrFlagsType attr = ParseFunctionAttributes(); Id *funcName = (_token == TK_IDENTIFIER) ? (Id *)Expect(TK_IDENTIFIER) : generateSurrogateFunctionName(); Expect('('); FunctionExpr *f = CreateFunction(start, funcName, lambda); f->setPure(attr & FATTR_PURE); f->setNodiscard(attr & FATTR_NODISCARD); return f; } ClassExpr* SQParser::ClassExp(SourceLoc classStart, Expr *key) { NestingChecker nc(this); Expr *baseExpr = NULL; if(_token == TK_EXTENDS) { Lex(); baseExpr = Expression(SQE_RVALUE); } else if (_token == '(') { Lex(); baseExpr = Expression(SQE_RVALUE); Expect(')'); } checkBraceIndentationStyle(); Expect('{'); ClassExpr *d = newNode(arena(), classStart, key, baseExpr); _docObjectStack.push_back(&d->docObject); ParseTableOrClass(d, ';','}'); _docObjectStack.pop_back(); return d; } Expr* SQParser::DeleteExpr() { NestingChecker nc(this); SourceLoc opStart = _lex.tokenStart(); Consume(TK_DELETE); Expr *arg = PrefixedExpr(); return newNode(TO_DELETE, opStart, arg); } Expr* SQParser::PrefixIncDec(SQInteger token) { NestingChecker nc(this); SourceLoc opStart = _lex.tokenStart(); SQInteger diff = (token==TK_MINUSMINUS) ? -1 : 1; Lex(); Expr *arg = PrefixedExpr(); return newNode(arg, diff, IF_PREFIX, opStart); } static bool can_be_type_name(int token) { return token == TK_IDENTIFIER || token == TK_NULL || token == TK_FUNCTION || token == TK_CLASS; } unsigned SQParser::parseTypeMask(bool eol_breaks_type_parsing) { unsigned typeMask = 0; Lex(); bool requireParen = false; if (_token == '(') { requireParen = true; Lex(); } if (!can_be_type_name(_token)) reportDiagnostic(DiagnosticsId::DI_EXPECTED_TOKEN, "TYPE_NAME"); while (can_be_type_name(_token)) { const char* suggestion = nullptr; SQUnsignedInteger32 currentMask = 0; const char* name = (_token == TK_IDENTIFIER) ? _lex._svalue : (_token == TK_NULL) ? "null" : (_token == TK_FUNCTION) ? "function" : (_token == TK_CLASS) ? "class" : ""; bool found = sq_type_string_to_mask(name, currentMask, suggestion); if (!found) { if (suggestion) reportDiagnostic(DiagnosticsId::DI_INVALID_TYPE_NAME_SUGGESTION, name, suggestion); else reportDiagnostic(DiagnosticsId::DI_INVALID_TYPE_NAME, name); } typeMask |= currentMask; Lex(); if (_lex._prevtoken == '\n' && eol_breaks_type_parsing && !requireParen) break; if (_token == '|') Lex(); else break; if (!can_be_type_name(_token)) reportDiagnostic(DiagnosticsId::DI_EXPECTED_TOKEN, "TYPE_NAME"); } if (requireParen) Expect(')'); return typeMask; } FunctionExpr* SQParser::CreateFunction(SourceLoc start, Id *name, bool lambda, bool ctor) { NestingChecker nc(this); FunctionExpr *f = ctor ? newNode(arena(), start, name->name()) : newNode(arena(), start, name); SQInteger defparams = 0; auto ¶ms = f->parameters(); while (_token!=')') { SourceSpan paramSpan = _lex.tokenSpan(); if (_token == TK_VARPARAMS) { if(defparams > 0) reportDiagnostic(DiagnosticsId::DI_VARARG_WITH_DEFAULT_ARG); f->addParameter(paramSpan, "vargv"); f->setVararg(true); params.back()->setVararg(); Lex(); unsigned typeMask = _token == ':' ? parseTypeMask(false) : ~0u; params.back()->setTypeMask(typeMask); if(_token != ')') reportDiagnostic(DiagnosticsId::DI_EXPECTED_TOKEN, ")"); break; } else { if (_token == '{' || _token == '[') { SourceSpan argSpan = _lex.tokenSpan(); SourceLoc keywordStart = _lex.tokenStart(); SQInteger destructurer = _token; constexpr int maxLen = 16; char buf[maxLen]; snprintf(buf, maxLen, "@arg%d", int(params.size() + 1)); Id *surrogateName = newId(buf); Lex(); // skip '{' or '[' DestructuringDecl *destrDecl = newNode(arena(), keywordStart, destructurer == '{' ? DT_TABLE : DT_ARRAY); destrDecl->setExpression(surrogateName); parseDestructuringFields(destrDecl); Expect(destructurer == '{' ? '}' : ']'); f->addParameter(argSpan, surrogateName->name(), nullptr); ParamDecl *p = params.back(); p->setDestructuring(destrDecl); p->setTypeMask(destructurer == '{' ? (_RT_TABLE | _RT_INSTANCE | _RT_CLASS) : (_RT_ARRAY)); } else { Id *paramname = (Id *)Expect(TK_IDENTIFIER); unsigned typeMask = _token == ':' ? parseTypeMask(false) : ~0u; Expr *defVal = NULL; if (_token == '=') { Lex(); defVal = Expression(SQE_RVALUE); defparams++; } else { if (defparams > 0) reportDiagnostic(DiagnosticsId::DI_EXPECTED_TOKEN, "="); } // Use Id's span for parameter name span f->addParameter(paramname->sourceSpan(), paramname->name(), defVal); //-V522 ParamDecl *p = params.back(); p->setTypeMask(typeMask); } if(_token == ',') Lex(); else if(_token != ')') reportDiagnostic(DiagnosticsId::DI_EXPECTED_TOKEN, ") or ,"); } } Expect(')'); unsigned resultTypeMask = _token == ':' ? parseTypeMask(false) : ~0u; Block *body = NULL; SQUnsignedInteger savedLangFeatures = _lang_features; _docObjectStack.push_back(&f->docObject); if (lambda) { Expr *expr = Expression(SQE_REGULAR); SourceSpan exprSpan = expr->sourceSpan(); // For lambda, use expression span as the synthetic "return" span ReturnStatement *retStmt = newNode(exprSpan, expr); retStmt->setIsLambda(); body = newNode(arena(), exprSpan.start); body->addStatement(retStmt); body->setSpanEnd(exprSpan.end); } else { if (_token != '{') reportDiagnostic(DiagnosticsId::DI_EXPECTED_TOKEN, "{"); checkBraceIndentationStyle(); body = (Block *)parseStatement(false); } _docObjectStack.pop_back(); f->setBody(body); f->setSourceName(_sourcename); f->setLambda(lambda); f->setResultTypeMask(resultTypeMask); _lang_features = savedLangFeatures; return f; } ImportStmt* SQParser::parseImportStatement() { if (!_are_imports_still_allowed) reportDiagnostic(DiagnosticsId::DI_GENERAL_COMPILE_ERROR, "import statements must be at the top of the file"); // Reuse lexer for the specific import syntax SourceLoc start = _lex.tokenStart(); ImportStmt *importStmt = nullptr; const char *keyword = _lex._svalue; if (strcmp(keyword, "from") == 0) { // from "module" import x, y, z Lex(); if (_token != TK_STRING_LITERAL) reportDiagnostic(DiagnosticsId::DI_EXPECTED_TOKEN, "module name"); const char *moduleName = copyString(_lex._svalue); Lex(); if (_token != TK_IDENTIFIER || strcmp(_lex._svalue, "import") != 0) reportDiagnostic(DiagnosticsId::DI_EXPECTED_TOKEN, "import"); Lex(); importStmt = newNode(arena(), start, moduleName, nullptr); importStmt->nameCol = _lex.tokenStart().column; importStmt->aliasCol = _lex.tokenStart().column; // Parse import list: x, y, z as foo, * do { SQModuleImportSlot slot{}; if (_token != '*' && _token != TK_IDENTIFIER) reportDiagnostic(DiagnosticsId::DI_EXPECTED_TOKEN, "identifier or *"); bool isWildcard = (_token == '*'); slot.name = copyString(isWildcard ? "*" : _lex._svalue); SourceLoc slotLoc = _lex.tokenStart(); slot.line = slotLoc.line; slot.column = slotLoc.column; Lex(); // Check for an alias if (_token == TK_IDENTIFIER && strcmp(_lex._svalue, "as") == 0) { Lex(); if (_token != TK_IDENTIFIER) reportDiagnostic(DiagnosticsId::DI_EXPECTED_TOKEN, "identifier"); if (isWildcard) reportDiagnostic(DiagnosticsId::DI_GENERAL_COMPILE_ERROR, "cannot rename *"); slot.alias = copyString(_lex._svalue); Lex(); } importStmt->slots.push_back(slot); if (_token == ',') Lex(); else break; } while (true); } else if (strcmp(keyword, "import") == 0) { // import "module" [as alias] Lex(); if (_token != TK_STRING_LITERAL) reportDiagnostic(DiagnosticsId::DI_EXPECTED_TOKEN, "module name string"); const char *moduleName = copyString(_lex._svalue); int32_t nameCol = _lex.tokenStart().column; int32_t aliasCol = nameCol; Lex(); const char *moduleAlias = nullptr; if (_token == TK_IDENTIFIER && strcmp(_lex._svalue, "as") == 0) { Lex(); if (_token != TK_IDENTIFIER) reportDiagnostic(DiagnosticsId::DI_EXPECTED_TOKEN, "identifier"); moduleAlias = copyString(_lex._svalue); aliasCol = _lex.tokenStart().column; Lex(); } importStmt = newNode(arena(), start, moduleName, moduleAlias); importStmt->nameCol = nameCol; importStmt->aliasCol = aliasCol; } if (importStmt) { // Set end position to current position (after the last parsed token) importStmt->setSpanEnd(_lex.tokenStart()); } return importStmt; } } // namespace SQCompilation #endif ================================================ FILE: squirrel/compiler/parser.h ================================================ #pragma once #include "sqpcheader.h" #ifndef NO_COMPILER #include #include "lexer.h" #include "lex_tokens.h" #include "sqvm.h" #include "compilationcontext.h" #include "ast.h" namespace SQCompilation { class SQParser { enum SQExpressionContext { SQE_REGULAR = 0, SQE_IF, SQE_SWITCH, SQE_LOOP_CONDITION, SQE_FUNCTION_ARG, SQE_RVALUE, SQE_ARRAY_ELEM, }; using FuncAttrFlagsType = uint8_t; enum FuncAttribute : FuncAttrFlagsType { FATTR_PURE = 0x01, FATTR_NODISCARD = 0x02, }; Arena *_astArena; template N *newNode(Args&&... args) const { return new (arena()) N(std::forward(args)...); } char *copyString(const char *s) const { size_t len = strlen(s); size_t memLen = (len + 1) * sizeof(char); char *buf = (char *)arena()->allocate(memLen); memcpy(buf, s, memLen); return buf; } Id *newId(const char *name) const { return newNode(_lex.tokenSpan(), copyString(name)); } LiteralExpr *newStringLiteral(const char *s) const { return newNode(_lex.tokenSpan(), copyString(s)); } Arena *arena() const { return _astArena; } void checkSuspiciousUnaryOp(SQInteger prevTok, SQInteger tok, unsigned prevFlags); void checkSuspiciousBracket(); public: SQCompilationContext &_ctx; void reportDiagnostic(int32_t id, ...); uint32_t _depth; SQParser(SQVM *v, const char *sourceText, size_t sourceTextSize, const char* sourcename, Arena *astArena, SQCompilationContext &ctx, Comments *comments); bool ProcessPosDirective(); void Lex(); void checkBraceIndentationStyle(); void Consume(SQInteger tok) { assert(tok == _token); Lex(); } Expr* Expect(SQInteger tok); bool IsEndOfStatement() const { return ((_lex._prevtoken == '\n') || (_token == SQUIRREL_EOB) || (_token == '}') || (_token == ';')) || (_token == TK_DIRECTIVE); } void OptionalSemicolon(); RootBlock* parse(); Block* parseStatements(SourceLoc start); Statement* parseStatement(bool closeframe = true); Expr* parseCommaExpr(SQExpressionContext expression_context); Expr* Expression(SQExpressionContext expression_context); template Expr *BIN_EXP(T f, enum TreeOp top, Expr *lhs); Expr* LogicalNullCoalesceExp(); Expr* LogicalOrExp(); Expr* LogicalAndExp(); Expr* BitwiseOrExp(); Expr* BitwiseXorExp(); Expr* BitwiseAndExp(); Expr* EqExp(); Expr* CompExp(); Expr* ShiftExp(); Expr* PlusExp(); Expr* MultExp(); Expr* PrefixedExpr(); Expr* Factor(SQInteger &pos); void ParseTableOrClass(TableExpr *decl, SQInteger separator, SQInteger terminator); Decl *parseLocalDeclStatement(bool onlySingleVariable = false); Decl *parseLocalFunctionExprStmt(bool assignable, SourceLoc keywordStart); Decl *parseLocalClassExprStmt(bool assignable, SourceLoc keywordStart); void parseDestructuringFields(DestructuringDecl *destr); Statement* IfLikeBlock(bool &wrapped); Statement* parseIfStatement(); WhileStatement* parseWhileStatement(); DoWhileStatement* parseDoWhileStatement(); ForStatement* parseForStatement(); ForeachStatement* parseForEachStatement(); SwitchStatement* parseSwitchStatement(); Expr *parseStringTemplate(); unsigned parseTypeMask(bool eol_breaks_type_parsing); LiteralExpr* ExpectScalar(); ConstDecl* parseConstStatement(bool global, SourceLoc globalStart = SourceLoc::invalid()); ConstDecl* parseConstFunctionExprStmt(bool global, SourceLoc globalStart = SourceLoc::invalid()); EnumDecl* parseEnumStatement(bool global, SourceLoc globalStart = SourceLoc::invalid()); TryStatement* parseTryCatchStatement(); Id* generateSurrogateFunctionName(); FunctionExpr* FunctionExp(bool lambda); FuncAttrFlagsType ParseFunctionAttributes(); ClassExpr* ClassExp(SourceLoc classStart, Expr *key); Expr* DeleteExpr(); Expr* PrefixIncDec(SQInteger token); FunctionExpr* CreateFunction(SourceLoc start, Id *name, bool lambda = false, bool ctor = false); Statement* parseDirectiveStatement(); ImportStmt* parseImportStatement(); void onDocString(const char *doc_string); DocObject& getModuleDocObject() { return _moduleDocObject; } private: ArenaVector _docObjectStack; DocObject _moduleDocObject; SQInteger _token; const char *_sourcename; SQLexer _lex; SQExpressionContext _expression_context; SQUnsignedInteger _lang_features; SQVM *_vm; bool _are_imports_still_allowed = true; uint32_t _foreachDestrCounter = 0; }; } // namespace SQCompilation #endif ================================================ FILE: squirrel/compiler/sourceloc.h ================================================ #pragma once #ifndef _SQSOURCELOC_H_ #define _SQSOURCELOC_H_ #include #include namespace SQCompilation { struct SourceLoc { int32_t line; // 1-based int32_t column; // 0-based static SourceLoc invalid() { return {-1, -1}; } bool isValid() const { return line >= 0; } bool operator==(const SourceLoc &other) const { return line == other.line && column == other.column; } bool operator!=(const SourceLoc &other) const { return !(*this == other); } }; struct SourceSpan { SourceLoc start; SourceLoc end; // Exclusive (one past last char) static SourceSpan invalid() { return {SourceLoc::invalid(), SourceLoc::invalid()}; } bool isValid() const { return start.isValid() && end.isValid(); } static SourceSpan merge(const SourceSpan &a, const SourceSpan &b) { return {a.start, b.end}; } int32_t textWidth() const { if (start.line != end.line) return 1; return std::max(1, end.column - start.column); } bool operator==(const SourceSpan &other) const { return start == other.start && end == other.end; } bool operator!=(const SourceSpan &other) const { return !(*this == other); } }; } // namespace SQCompilation #endif // _SQSOURCELOC_H_ ================================================ FILE: squirrel/compiler/sqdump.cpp ================================================ /* see copyright notice in squirrel.h */ #include "sqpcheader.h" #ifndef NO_COMPILER #include "sqstring.h" #include "sqfuncproto.h" #include "sqtable.h" #include "opcodes.h" #include "sqvm.h" #include "sqio.h" #include "sqclosure.h" #include "sqarray.h" #include #define SQ_OPCODE(id) {#id}, SQInstructionDesc g_InstrDesc[]={ SQ_OPCODES_LIST }; #undef SQ_OPCODE static void streamprintf(OutputStream *stream, const char *fmt, ...) { static char buffer[4096] = { 0 }; va_list vl; va_start(vl, fmt); vsnprintf(buffer, 4096, fmt, vl); va_end(vl); stream->writeString(buffer); } static void DumpLiteral(OutputStream *stream, const SQObjectPtr &o) { switch (sq_type(o)) { case OT_NULL: streamprintf(stream, "null"); break; case OT_STRING: streamprintf(stream, "string(\"%s\")", _stringval(o)); break; case OT_FLOAT: streamprintf(stream, "float(%1.9g)", _float(o)); break; case OT_INTEGER: streamprintf(stream, "int(" _PRINT_INT_FMT ")", _integer(o)); break; case OT_BOOL: streamprintf(stream, "%s", _integer(o) ? "bool(true)" : "bool(false)"); break; case OT_ARRAY: streamprintf(stream, "array(0x%p size=%d)", (void*)_rawval(o), int(_array(o)->Size())); break; case OT_TABLE: streamprintf(stream, "table(0x%p size=%d)", (void*)_rawval(o), int(_table(o)->CountUsed())); break; case OT_CLOSURE: { SQFunctionProto *func = _closure(o)->_function; const char *funcName = sq_isstring(func->_name) ? _stringval(func->_name) : ""; streamprintf(stream, "%s(0x%p \"%s\")", GetTypeName(o), (void*)func, funcName); break; } case OT_NATIVECLOSURE: { SQNativeClosure *nc = _nativeclosure(o); const char* funcName = sq_isstring(nc->_name) ? _stringval(nc->_name) : ""; streamprintf(stream, "%s(0x%p \"%s\")", GetTypeName(o), (void*)nc, funcName); break; } case OT_FUNCPROTO: { SQFunctionProto *func = _funcproto(o); const char* funcName = sq_isstring(func->_name) ? _stringval(func->_name) : ""; streamprintf(stream, "%s(0x%p \"%s\")", GetTypeName(o), (void*)func, funcName); break; } case OT_GENERATOR: { SQGenerator *gen = _generator(o); SQObjectPtr nameObj = _closure(gen->_closure)->_function->_name; const char* funcName = sq_isstring(nameObj) ? _stringval(nameObj) : ""; streamprintf(stream, "%s(0x%p \"%s\")", GetTypeName(o), (void*)gen, funcName); break; } default: streamprintf(stream, "%s(0x%p)", GetTypeName(o), (void*)_rawval(o)); break; } } static uint64_t GetUInt64FromPtr(const void *ptr) { uint64_t res = 0; for (int i = 0; i < 8; i++) res += uint64_t(((const uint8_t *)ptr)[i]) << (i * 8); return res; } void DumpInstructions(OutputStream *stream, SQLineInfosHeader *lineinfos, int nlineinfos, const SQInstruction *ii, SQInt32 i_count, const SQObjectPtr *_literals, SQInt32 _nliterals, const SQObjectPtr *_functions, SQInt32 _nfunctions, int instruction_index) { SQInt32 n = 0; int lineHint = 0; for (int i = 0; i < i_count; i++) { const SQInstruction &inst = ii[i]; bool isStepPoint = false; char curInstr = instruction_index == i ? '>' : ' '; SQInteger line = SQFunctionProto::GetLine(lineinfos, nlineinfos, i, &lineHint, &isStepPoint); if (inst.op == _OP_LOAD || inst.op == _OP_DLOAD || inst.op == _OP_PREPCALLK || inst.op == _OP_GETK) { SQInteger lidx = inst._arg1; streamprintf(stream, "[line%c%03d]%c[op %03d] %15s %d ", isStepPoint ? '-' : ' ', (SQInt32)line, curInstr, (SQInt32)n, g_InstrDesc[inst.op].name, inst._arg0); if (lidx >= 0xFFFFFFFF) //-V547 streamprintf(stream, "null"); else { assert(lidx < _nliterals); SQObjectPtr key = _literals[lidx]; DumpLiteral(stream, key); } if (inst.op != _OP_DLOAD) { streamprintf(stream, " %d %d", inst._arg2, inst._arg3); } else { streamprintf(stream, " %d ", inst._arg2); lidx = inst._arg3; if (lidx >= 0xFFFFFFFF) //-V547 streamprintf(stream, "null"); else { assert(lidx < _nliterals); SQObjectPtr key = _literals[lidx]; DumpLiteral(stream, key); } } } /* else if(inst.op==_OP_ARITH){ printf("[%03d] %15s %d %d %d %c\n",n,g_InstrDesc[inst.op].name,inst._arg0,inst._arg1,inst._arg2,inst._arg3); }*/ else { int jumpLabel = 0x7FFFFFFF; switch (inst.op) { case _OP_JMP: jumpLabel = i + inst._arg1 + 1; break; default: break; } bool arg1F = inst.op == _OP_JCMPF || inst.op == _OP_LOADFLOAT; if (arg1F) streamprintf(stream, "[line%c%03d]%c[op %03d] %15s %d %1.9g %d %d", isStepPoint ? '-' : ' ', (SQInt32)line, curInstr, (SQInt32)n, g_InstrDesc[inst.op].name, inst._arg0, inst._farg1, inst._arg2, inst._arg3); else { if (i > 0 && (ii[i - 1].op == _OP_SET_LITERAL || ii[i - 1].op == _OP_GET_LITERAL)) streamprintf(stream, "[line%c%03d]%c[op %03d] HINT (0x%" PRIx64 ")", isStepPoint ? '-' : ' ', (SQInt32)line, curInstr, (SQInt32)n, GetUInt64FromPtr(ii + i)); else if (inst.op < sizeof(g_InstrDesc) / sizeof(g_InstrDesc[0])) streamprintf(stream, "[line%c%03d]%c[op %03d] %15s %d %d %d %d", isStepPoint ? '-' : ' ', (SQInt32)line, curInstr, (SQInt32)n, g_InstrDesc[inst.op].name, inst._arg0, inst._arg1, inst._arg2, inst._arg3); else streamprintf(stream, "[line%c%03d]%c[op %03d] INVALID INSTRUNCTION (%d)", isStepPoint ? '-' : ' ', (SQInt32)line, curInstr, (SQInt32)n, int(inst.op)); } if (jumpLabel != 0x7FFFFFFF) streamprintf(stream, " // jump to %d", jumpLabel); } // comments switch (inst.op) { case _OP_LOAD: case _OP_LOADFLOAT: case _OP_LOADINT: case _OP_LOADBOOL: case _OP_LOADROOT: case _OP_LOADCALLEE: streamprintf(stream, " // -> r%d", int(inst._arg0)); break; case _OP_LOADNULLS: streamprintf(stream, " // null -> [r%d .. r%d]", int(inst._arg0), int(inst._arg0 + inst._arg1 - 1)); break; case _OP_DLOAD: { streamprintf(stream, " //"); if (unsigned(inst._arg1) < unsigned(_nliterals)) { const SQObjectPtr &key = _literals[inst._arg1]; DumpLiteral(stream, key); } else { streamprintf(stream, "?"); } streamprintf(stream, " -> r%d, ", int(inst._arg0)); if (unsigned(inst._arg3) < unsigned(_nliterals)) { const SQObjectPtr &key = _literals[inst._arg3]; DumpLiteral(stream, key); } else { streamprintf(stream, "?"); } streamprintf(stream, " -> r%d", int(inst._arg2)); break; } case _OP_DATA_NOP: { int saveInstr = i + ((inst._arg2 << 8) + inst._arg3); if ((inst._arg0 || inst._arg1 || inst._arg2 || inst._arg3) && saveInstr > 0 && saveInstr < i_count && ii[saveInstr].op == _OP_SAVE_STATIC_MEMO) { int loadInstr = saveInstr + 1 - ((ii[saveInstr]._arg2 << 8) + ii[saveInstr]._arg3); if (loadInstr == i) { streamprintf(stream, " // LOAD_STATIC_MEMO: staticmemo[%d] -> r%d, jump to %d%s", int(inst._arg1 & STATIC_MEMO_IDX_MASK), int(inst._arg0), saveInstr + 1, (inst._arg1 & STATIC_MEMO_AUTO_FLAG) ? " (auto)" : ""); } } } break; case _OP_SAVE_STATIC_MEMO: { int loadInstr = i + 1 - ((inst._arg2 << 8) + inst._arg3); streamprintf(stream, " // r%d -> staticmemo[%d], r%d -> r%d, modify instr at %d%s", int(inst._arg0), int(inst._arg1 & STATIC_MEMO_IDX_MASK), int(inst._arg0), int(ii[loadInstr]._arg0), loadInstr, (inst._arg1 & STATIC_MEMO_AUTO_FLAG) ? " (auto)" : ""); } break; case _OP_CALL: streamprintf(stream, " // (call r%d) -> r%d", int(inst._arg1), int(inst._arg0)); break; case _OP_NULLCALL: streamprintf(stream, " // (if r%d then call r%d) -> r%d", int(inst._arg1), int(inst._arg1), int(inst._arg0)); break; case _OP_MOVE: streamprintf(stream, " // r%d -> r%d", int(inst._arg1), int(inst._arg0)); break; case _OP_GET: streamprintf(stream, " // r%d[r%d] -> r%d", int(inst._arg2), int(inst._arg1), int(inst._arg0)); break; case _OP_NEWOBJ: { const char *objType = inst._arg3 == NEWOBJ_TABLE ? "table" : (inst._arg3 == NEWOBJ_ARRAY ? "array" : (inst._arg3 == NEWOBJ_CLASS ? "class" : "unknown")); streamprintf(stream, " // new %s -> r%d", objType, int(inst._arg0)); break; } case _OP_CLOSURE: { streamprintf(stream, " // closure "); if (unsigned(inst._arg1) < unsigned(_nfunctions) && sq_isfunction(_functions[inst._arg1])) { SQFunctionProto *fp = _funcproto(_functions[inst._arg1]); if (sq_type(fp->_name) == OT_STRING) streamprintf(stream, "%s", _stringval(fp->_name)); } streamprintf(stream, " -> r%d", int(inst._arg0)); break; } case _OP_APPENDARRAY: { streamprintf(stream, " // r%d.append(", int(inst._arg0)); switch (inst._arg2) { case AAT_STACK: streamprintf(stream, "r%d", int(inst._arg1)); break; case AAT_LITERAL: if (unsigned(inst._arg1) < unsigned(_nliterals)) { const SQObjectPtr &key = _literals[inst._arg1]; DumpLiteral(stream, key); } else { streamprintf(stream, "?"); } break; case AAT_INT: streamprintf(stream, "%d", int(inst._arg1)); break; case AAT_FLOAT: streamprintf(stream, "%1.9g", *((const SQFloat *)&inst._arg1)); break; case AAT_BOOL: streamprintf(stream, "%s", inst._arg1 ? "true" : "false"); break; default: streamprintf(stream, "unknown"); break; } streamprintf(stream, ")"); break; } case _OP_GETK: case _OP_GET_LITERAL: { streamprintf(stream, " // r%d.[", int(inst._arg2)); if (unsigned(inst._arg1) < unsigned(_nliterals)) { const SQObjectPtr &key = _literals[inst._arg1]; DumpLiteral(stream, key); } else { streamprintf(stream, "?"); } streamprintf(stream, "] -> r%d", int(inst._arg0)); break; } case _OP_SET: if (inst._arg0 != 0xFF) streamprintf(stream, " // r%d[r%d] = r%d -> r%d", int(inst._arg2), int(inst._arg1), int(inst._arg3), int(inst._arg0)); else streamprintf(stream, " // r%d[r%d] = r%d", int(inst._arg2), int(inst._arg1), int(inst._arg3)); break; case _OP_SETK: case _OP_SET_LITERAL: { streamprintf(stream, " // r%d[", int(inst._arg2)); if (unsigned(inst._arg1) < unsigned(_nliterals)) { const SQObjectPtr &key = _literals[inst._arg1]; DumpLiteral(stream, key); } else { streamprintf(stream, "?"); } if (inst._arg0 != 0xFF) streamprintf(stream, "] = r%d -> r%d", int(inst._arg3), int(inst._arg0)); else streamprintf(stream, "] = r%d", int(inst._arg3)); break; } case _OP_SETI: if (inst._arg0 != 0xFF) streamprintf(stream, " // r%d[%d] = r%d -> r%d", int(inst._arg2), int(inst._arg1), int(inst._arg3), int(inst._arg0)); else streamprintf(stream, " // r%d[%d] = r%d", int(inst._arg2), int(inst._arg1), int(inst._arg3)); break; case _OP_NEWSLOT: streamprintf(stream, " // r%d[r%d] <- r%d", int(inst._arg2), int(inst._arg1), int(inst._arg3)); break; case _OP_NEWSLOTK: { streamprintf(stream, " // r%d[", int(inst._arg2)); if (unsigned(inst._arg1) < unsigned(_nliterals)) { const SQObjectPtr &key = _literals[inst._arg1]; DumpLiteral(stream, key); } else { streamprintf(stream, "?"); } streamprintf(stream, "] <- r%d", int(inst._arg3)); break; } case _OP_NEWSLOTA: streamprintf(stream, " // r%d[r%d] <- r%d %s", int(inst._arg1), int(inst._arg2), int(inst._arg3), (inst._arg0 & NEW_SLOT_STATIC_FLAG) ? " static" : ""); break; case _OP_DELETE: streamprintf(stream, " // delete r%d[r%d] -> r%d", int(inst._arg1), int(inst._arg2), int(inst._arg0)); break; case _OP_PREPCALL: streamprintf(stream, " // r%d[r%d] -> func=r%d, this=r%d", int(inst._arg2), int(inst._arg1), int(inst._arg0), int(inst._arg3)); break; case _OP_PREPCALLK: { streamprintf(stream, " // r%d[", int(inst._arg2)); if (unsigned(inst._arg1) < unsigned(_nliterals)) { const SQObjectPtr &key = _literals[inst._arg1]; DumpLiteral(stream, key); } else { streamprintf(stream, "?"); } streamprintf(stream, "] -> func=r%d, this=r%d", int(inst._arg0), int(inst._arg3)); break; } case _OP_TAILCALL: streamprintf(stream, " // tailcall r%d(%d args) -> r%d", int(inst._arg1), int(inst._arg3), int(inst._arg0)); break; case _OP_DMOVE: streamprintf(stream, " // r%d -> r%d, r%d -> r%d", int(inst._arg1), int(inst._arg0), int(inst._arg3), int(inst._arg2)); break; case _OP_RETURN: if (inst._arg0 != 0xFF) streamprintf(stream, " // return r%d", int(inst._arg1)); else streamprintf(stream, " // return null"); break; case _OP_EQ: case _OP_NE: { const char *op = inst.op == _OP_EQ ? "==" : "!="; if (inst._arg3) { streamprintf(stream, " // r%d %s ", int(inst._arg2), op); if (unsigned(inst._arg1) < unsigned(_nliterals)) { const SQObjectPtr &key = _literals[inst._arg1]; DumpLiteral(stream, key); } else { streamprintf(stream, "?"); } streamprintf(stream, " -> r%d", int(inst._arg0)); } else streamprintf(stream, " // r%d %s r%d -> r%d", int(inst._arg2), op, int(inst._arg1), int(inst._arg0)); break; } case _OP_ADD: streamprintf(stream, " // r%d + r%d -> r%d", int(inst._arg2), int(inst._arg1), int(inst._arg0)); break; case _OP_SUB: streamprintf(stream, " // r%d - r%d -> r%d", int(inst._arg2), int(inst._arg1), int(inst._arg0)); break; case _OP_MUL: streamprintf(stream, " // r%d * r%d -> r%d", int(inst._arg2), int(inst._arg1), int(inst._arg0)); break; case _OP_DIV: streamprintf(stream, " // r%d / r%d -> r%d", int(inst._arg2), int(inst._arg1), int(inst._arg0)); break; case _OP_MOD: streamprintf(stream, " // r%d %% r%d -> r%d", int(inst._arg2), int(inst._arg1), int(inst._arg0)); break; case _OP_ADDI: streamprintf(stream, " // r%d + %d -> r%d", int(inst._arg2), int(inst._arg1), int(inst._arg0)); break; case _OP_BITW: { const char *bwop = "?"; switch (inst._arg3) { case BW_AND: bwop = "&"; break; case BW_OR: bwop = "|"; break; case BW_XOR: bwop = "^"; break; case BW_SHIFTL: bwop = "<<"; break; case BW_SHIFTR: bwop = ">>"; break; case BW_USHIFTR: bwop = ">>>"; break; } streamprintf(stream, " // r%d %s r%d -> r%d", int(inst._arg2), bwop, int(inst._arg1), int(inst._arg0)); break; } case _OP_NEG: streamprintf(stream, " // -r%d -> r%d", int(inst._arg1), int(inst._arg0)); break; case _OP_NOT: streamprintf(stream, " // !r%d -> r%d", int(inst._arg1), int(inst._arg0)); break; case _OP_BWNOT: streamprintf(stream, " // ~r%d -> r%d", int(inst._arg1), int(inst._arg0)); break; case _OP_CMP: { const char *cmpop = "?"; switch (inst._arg3) { case CMP_G: cmpop = ">"; break; case CMP_GE: cmpop = ">="; break; case CMP_L: cmpop = "<"; break; case CMP_LE: cmpop = "<="; break; case CMP_3W: cmpop = "<=>"; break; } streamprintf(stream, " // r%d %s r%d -> r%d", int(inst._arg2), cmpop, int(inst._arg1), int(inst._arg0)); break; } case _OP_EXISTS: streamprintf(stream, " // r%d in r%d -> r%d", int(inst._arg2), int(inst._arg1), int(inst._arg0)); break; case _OP_INSTANCEOF: streamprintf(stream, " // r%d instanceof r%d -> r%d", int(inst._arg2), int(inst._arg1), int(inst._arg0)); break; case _OP_AND: streamprintf(stream, " // if r%d is false: r%d -> r%d, jump to %d", int(inst._arg2), int(inst._arg2), int(inst._arg0), i + inst._arg1 + 1); break; case _OP_OR: streamprintf(stream, " // if r%d is true: r%d -> r%d, jump to %d", int(inst._arg2), int(inst._arg2), int(inst._arg0), i + inst._arg1 + 1); break; case _OP_NULLCOALESCE: streamprintf(stream, " // if r%d != null: r%d -> r%d, jump to %d", int(inst._arg2), int(inst._arg2), int(inst._arg0), i + inst._arg1); break; case _OP_GETOUTER: streamprintf(stream, " // outer[%d] -> r%d", int(inst._arg1), int(inst._arg0)); break; case _OP_SETOUTER: if (inst._arg0 != 0xFF) streamprintf(stream, " // r%d -> outer[%d] -> r%d", int(inst._arg2), int(inst._arg1), int(inst._arg0)); else streamprintf(stream, " // r%d -> outer[%d]", int(inst._arg2), int(inst._arg1)); break; case _OP_INC: streamprintf(stream, " // r%d[r%d] += %d -> r%d", int(inst._arg1), int(inst._arg2), int(inst._sarg3()), int(inst._arg0)); break; case _OP_INCL: streamprintf(stream, " // r%d += %d", int(inst._arg1), int(inst._sarg3())); break; case _OP_PINC: streamprintf(stream, " // r%d[r%d] (old -> r%d) += %d", int(inst._arg1), int(inst._arg2), int(inst._arg0), int(inst._sarg3())); break; case _OP_PINCL: streamprintf(stream, " // r%d (old -> r%d) += %d", int(inst._arg1), int(inst._arg0), int(inst._sarg3())); break; case _OP_COMPARITH: { int selfidx = (unsigned(inst._arg1) >> 16) & 0xFFFF; int validx = unsigned(inst._arg1) & 0xFFFF; streamprintf(stream, " // r%d[r%d] %c= r%d -> r%d", selfidx, int(inst._arg2), char(inst._arg3), validx, int(inst._arg0)); break; } case _OP_COMPARITH_K: { int selfidx = (unsigned(inst._arg1) >> 16) & 0xFFFF; int validx = unsigned(inst._arg1) & 0xFFFF; streamprintf(stream, " // r%d.[", selfidx); if (unsigned(inst._arg2) < unsigned(_nliterals)) { const SQObjectPtr &key = _literals[inst._arg2]; DumpLiteral(stream, key); } else { streamprintf(stream, "?"); } streamprintf(stream, "] %c= r%d -> r%d", char(inst._arg3), validx, int(inst._arg0)); break; } case _OP_YIELD: if (inst._arg1 != 0xFF) streamprintf(stream, " // yield r%d", int(inst._arg1)); else streamprintf(stream, " // yield null"); break; case _OP_RESUME: streamprintf(stream, " // resume r%d -> r%d", int(inst._arg1), int(inst._arg0)); break; case _OP_CLONE: streamprintf(stream, " // clone r%d -> r%d", int(inst._arg1), int(inst._arg0)); break; case _OP_TYPEOF: streamprintf(stream, " // typeof r%d -> r%d", int(inst._arg1), int(inst._arg0)); break; case _OP_THROW: streamprintf(stream, " // throw r%d", int(inst._arg0)); break; case _OP_GETBASE: streamprintf(stream, " // base -> r%d", int(inst._arg0)); break; case _OP_CLOSE: streamprintf(stream, " // close outers from r%d", int(inst._arg1)); break; case _OP_PREFOREACH: streamprintf(stream, " // init iter r%d -> [r%d..r%d], if empty jump to %d", int(inst._arg0), int(inst._arg2), int(inst._arg2 + 2), i + inst._arg1 + 1); break; case _OP_FOREACH: streamprintf(stream, " // iter r%d next -> key=r%d, val=r%d, jump to %d", int(inst._arg0), int(inst._arg2), int(inst._arg2 + 1), i + inst._arg1 + 1); break; case _OP_POSTFOREACH: streamprintf(stream, " // if iter r%d dead jump to %d", int(inst._arg0), i + inst._arg1 + 1); break; case _OP_FREEZE: streamprintf(stream, " // freeze r%d -> r%d", int(inst._arg1), int(inst._arg0)); break; case _OP_CHECK_TYPE: streamprintf(stream, " // check r%d type mask=0x%X", int(inst._arg0), int(inst._arg1)); break; case _OP_JZ: if (inst._arg2) streamprintf(stream, " // if r%d jump to %d", int(inst._arg0), i + inst._arg1 + 1); else streamprintf(stream, " // if not r%d jump to %d", int(inst._arg0), i + inst._arg1 + 1); break; case _OP_JCMP: { const char *cmpop = "?"; bool negate = !(inst._arg3 >> 3); switch (inst._arg3 & 0x7) { case CMP_G: cmpop = negate ? "<=" : ">"; break; case CMP_GE: cmpop = negate ? "<" : ">="; break; case CMP_L: cmpop = negate ? ">=" : "<"; break; case CMP_LE: cmpop = negate ? ">" : "<="; break; case CMP_3W: cmpop = "<=>"; break; } streamprintf(stream, " // if r%d %s r%d jump to %d", int(inst._arg2), cmpop, int(inst._arg0), i + inst._arg1 + 1); break; } case _OP_JCMPK: { const char *cmpop = "?"; bool negate = !(inst._arg3 >> 3); switch (inst._arg3 & 0x7) { case CMP_G: cmpop = negate ? "<=" : ">"; break; case CMP_GE: cmpop = negate ? "<" : ">="; break; case CMP_L: cmpop = negate ? ">=" : "<"; break; case CMP_LE: cmpop = negate ? ">" : "<="; break; case CMP_3W: cmpop = "<=>"; break; } streamprintf(stream, " // if r%d %s ", int(inst._arg2), cmpop); if (unsigned(inst._arg0) < unsigned(_nliterals)) { const SQObjectPtr &key = _literals[inst._arg0]; DumpLiteral(stream, key); } else { streamprintf(stream, "?"); } streamprintf(stream, " jump to %d", i + inst._arg1 + 1); break; } case _OP_JCMPI: { const char *cmpop = "?"; bool negate = !(inst._arg3 >> 3); switch (inst._arg3 & 0x7) { case CMP_G: cmpop = negate ? "<=" : ">"; break; case CMP_GE: cmpop = negate ? "<" : ">="; break; case CMP_L: cmpop = negate ? ">=" : "<"; break; case CMP_LE: cmpop = negate ? ">" : "<="; break; case CMP_3W: cmpop = "<=>"; break; } streamprintf(stream, " // if r%d %s %d jump to %d", int(inst._arg2), cmpop, int(inst._arg1), i + inst._sarg0() + 1); break; } case _OP_JCMPF: { const char *cmpop = "?"; bool negate = !(inst._arg3 >> 3); switch (inst._arg3 & 0x7) { case CMP_G: cmpop = negate ? "<=" : ">"; break; case CMP_GE: cmpop = negate ? "<" : ">="; break; case CMP_L: cmpop = negate ? ">=" : "<"; break; case CMP_LE: cmpop = negate ? ">" : "<="; break; case CMP_3W: cmpop = "<=>"; break; } streamprintf(stream, " // if r%d %s %g jump to %d", int(inst._arg2), cmpop, inst._farg1, i + inst._sarg0() + 1); break; } case _OP_LOAD_STATIC_MEMO: streamprintf(stream, " // staticmemo[%d] -> r%d, jump to %d", int(inst._arg1), int(inst._arg0), i + (inst._arg2 << 8) + inst._arg3 + 1); break; case _OP_PUSHTRAP: streamprintf(stream, " // catch -> r%d, jump to %d", int(inst._arg0), i + inst._arg1 + 1); break; case _OP_POPTRAP: streamprintf(stream, " // pop %d trap(s)", int(inst._arg0)); break; case _OP_PATCH_DOCOBJ: streamprintf(stream, " // patch docobj r%d", int(inst._arg0)); break; default: break; } streamprintf(stream, "\n"); n++; } } static void DumpLiterals(OutputStream *stream, const SQObjectPtr *_literals, SQInt32 _nliterals) { streamprintf(stream, "-----LITERALS\n"); for (SQInt32 i = 0; i < _nliterals; ++i) { streamprintf(stream, "[%d] ", (SQInt32)i); DumpLiteral(stream, _literals[i]); streamprintf(stream, "\n"); } } static void DumpStaticMemos(OutputStream *stream, const SQObjectPtr *_staticmemos, SQInt32 _nstaticmemos) { streamprintf(stream, "-----STATIC MEMOS\n"); for (SQInt32 i = 0; i < _nstaticmemos; ++i) { streamprintf(stream, "[%d] ", (SQInt32)i); DumpLiteral(stream, _staticmemos[i]); streamprintf(stream, "\n"); } } static void DumpLocals(OutputStream *stream, const SQLocalVarInfo *_localvarinfos, SQInt32 _nlocalvarinfos) { streamprintf(stream, "-----LOCALS\n"); for (SQInt32 si = 0; si < _nlocalvarinfos; si++) { SQLocalVarInfo lvi = _localvarinfos[si]; streamprintf(stream, "[%d] %s \t%d %d\n", (SQInt32)lvi._pos, _stringval(lvi._name), (SQInt32)lvi._start_op, (SQInt32)lvi._end_op); } } static void DumpLineInfo(OutputStream *stream, const SQLineInfosHeader *_lineinfos, SQInt32 _nlineinfos) { streamprintf(stream, "-----LINE INFO\n"); for (SQInt32 i = 0; i < _nlineinfos; i++) { int op = 0; int line = 0; bool isDbgStepPoint = false; if (_lineinfos->_is_compressed) { SQCompressedLineInfo li = ((SQCompressedLineInfo *)(void *)(_lineinfos + 1))[i]; op = li._op; line = _lineinfos->_first_line + li._line_offset; isDbgStepPoint = li._is_dbg_step_point; } else { SQFullLineInfo li = ((SQFullLineInfo *)(void *)(_lineinfos + 1))[i]; op = li._op; line = _lineinfos->_first_line + li._line_offset; isDbgStepPoint = li._is_dbg_step_point; } streamprintf(stream, "op [%d] line [%d]%s\n", op, line, isDbgStepPoint ? " dbgstep" : ""); } } void Dump(OutputStream *stream, SQFunctionProto *func, bool deep, int instruction_index) { //if (!dump_enable) return ; SQUnsignedInteger n = 0, i; if (deep) { for (i = 0; i < func->_nfunctions; ++i) { SQObjectPtr &f = func->_functions[i]; assert(sq_isfunction(f)); Dump(stream, _funcproto(f), deep, -1); } } streamprintf(stream, "SQInstruction sizeof %d\n", (SQInt32)sizeof(SQInstruction)); streamprintf(stream, "SQObject sizeof %d\n", (SQInt32)sizeof(SQObject)); streamprintf(stream, "--------------------------------------------------------------------\n"); streamprintf(stream, "*****FUNCTION [%s]\n", sq_type(func->_name) == OT_STRING ? _stringval(func->_name) : "unknown"); DumpLiterals(stream, func->_literals, func->_nliterals); DumpStaticMemos(stream, func->_staticmemos, func->_nstaticmemos); streamprintf(stream, "-----RESULT TYPE MASK = 0x%X\n", func->_result_type_mask); streamprintf(stream, "-----PARAMS\n"); if (func->_varparams) streamprintf(stream, "<>\n"); n = 0; for (i = 0; i < func->_nparameters; i++) { streamprintf(stream, "[%d] ", (SQInt32)n); DumpLiteral(stream, func->_parameters[i]); streamprintf(stream, ", type mask = 0x%X\n", func->_param_type_masks[i]); n++; } DumpLocals(stream, func->_localvarinfos, func->_nlocalvarinfos); DumpLineInfo(stream, func->_lineinfos, func->_nlineinfos); streamprintf(stream, "-----dump\n"); DumpInstructions(stream, func->_lineinfos, func->_nlineinfos, func->_instructions, func->_ninstructions, func->_literals, func->_nliterals, func->_functions, func->_nfunctions, instruction_index); streamprintf(stream, "-----\n"); streamprintf(stream, "stack size[%d]\n", (SQInt32)func->_stacksize); streamprintf(stream, "--------------------------------------------------------------------\n\n"); } void Dump(SQFunctionProto *func, int instruction_index) { FileOutputStream fos(stdout); Dump(&fos, func, false, instruction_index); } #endif ================================================ FILE: squirrel/compiler/sqfuncstate.cpp ================================================ /* see copyright notice in squirrel.h */ #include "sqpcheader.h" #ifndef NO_COMPILER #include "sqstring.h" #include "sqfuncproto.h" #include "sqtable.h" #include "opcodes.h" #include "sqfuncstate.h" #include "sqio.h" #include "sqclosure.h" #include "sqarray.h" #include static void streamprintf(OutputStream *stream, const char *fmt, ...) { static char buffer[4096] = { 0 }; va_list vl; va_start(vl, fmt); vsnprintf(buffer, 4096, fmt, vl); va_end(vl); stream->writeString(buffer); } static void DumpLiteral(OutputStream *stream, const SQObjectPtr &o) { switch (sq_type(o)) { case OT_NULL: streamprintf(stream, "null"); break; case OT_STRING: streamprintf(stream, "string(\"%s\")", _stringval(o)); break; case OT_FLOAT: streamprintf(stream, "float(%1.9g)", _float(o)); break; case OT_INTEGER: streamprintf(stream, "int(" _PRINT_INT_FMT ")", _integer(o)); break; case OT_BOOL: streamprintf(stream, "%s", _integer(o) ? "bool(true)" : "bool(false)"); break; case OT_ARRAY: streamprintf(stream, "array(0x%p size=%d)", (void*)_rawval(o), int(_array(o)->Size())); break; case OT_TABLE: streamprintf(stream, "table(0x%p size=%d)", (void*)_rawval(o), int(_table(o)->CountUsed())); break; case OT_CLOSURE: { SQFunctionProto *func = _closure(o)->_function; const char *funcName = sq_isstring(func->_name) ? _stringval(func->_name) : ""; streamprintf(stream, "%s(0x%p \"%s\")", GetTypeName(o), (void*)func, funcName); break; } case OT_NATIVECLOSURE: { SQNativeClosure *nc = _nativeclosure(o); const char* funcName = sq_isstring(nc->_name) ? _stringval(nc->_name) : ""; streamprintf(stream, "%s(0x%p \"%s\")", GetTypeName(o), (void*)nc, funcName); break; } case OT_FUNCPROTO: { SQFunctionProto *func = _funcproto(o); const char* funcName = sq_isstring(func->_name) ? _stringval(func->_name) : ""; streamprintf(stream, "%s(0x%p \"%s\")", GetTypeName(o), (void*)func, funcName); break; } case OT_GENERATOR: { SQGenerator *gen = _generator(o); SQObjectPtr nameObj = _closure(gen->_closure)->_function->_name; const char* funcName = sq_isstring(nameObj) ? _stringval(nameObj) : ""; streamprintf(stream, "%s(0x%p \"%s\")", GetTypeName(o), (void*)gen, funcName); break; } default: streamprintf(stream, "%s(0x%p)", GetTypeName(o), (void*)_rawval(o)); break; } } SQFuncState::SQFuncState(SQSharedState *ss,SQFuncState *parent,SQCompilationContext &ctx) : _vlocals(ss->_alloc_ctx), _vlocals_info(ss->_alloc_ctx), _targetstack(ss->_alloc_ctx), _unresolvedbreaks(ss->_alloc_ctx), _unresolvedcontinues(ss->_alloc_ctx), _expr_block_results(ss->_alloc_ctx), _functions(ss->_alloc_ctx), _parameters(ss->_alloc_ctx), _param_type_masks(ss->_alloc_ctx), _outervalues(ss->_alloc_ctx), _outervalues_info(ss->_alloc_ctx), _instructions(ss->_alloc_ctx), _localvarinfos(ss->_alloc_ctx), _full_line_infos(ss->_alloc_ctx), _breaktargets(ss->_alloc_ctx), _continuetargets(ss->_alloc_ctx), _blockstacksizes(ss->_alloc_ctx), _defaultparams(ss->_alloc_ctx), _childstates(ss->_alloc_ctx), _ctx(ctx) { _nliterals = 0; _literals = SQTable::Create(ss,0); _sharedstate = ss; _lastline = 0; _optimization = true; _parent = parent; _stacksize = 0; _traps = 0; _returnexp = 0; _varparams = false; _bgenerator = false; _purefunction = false; _nodiscard = false; _outers = 0; _hoistLevel = 0; _staticmemos_count = 0; _ss = ss; _result_type_mask = ~0u; lang_features = parent ? parent->lang_features : ss->defaultLangFeatures; } void ResetStaticMemos(SQFunctionProto *func, SQSharedState *ss) { for (int i = 0; i < func->_nfunctions; i++) { SQObjectPtr &f = func->_functions[i]; assert(sq_isfunction(f)); ResetStaticMemos(_funcproto(f), ss); } if (func->_nstaticmemos) { SQInstruction* instr = func->_instructions; int count = func->_ninstructions; for (int i = 0; i < count; i += sq_opcode_length(instr[i].op)) { if (instr[i].op == _OP_LOAD_STATIC_MEMO) { SQInteger staticIdx = instr[i]._arg1; assert(unsigned(staticIdx) < func->_nstaticmemos); SQObjectPtr& storedStatic = func->_staticmemos[staticIdx]; if (ISREFCOUNTED(sq_type(storedStatic))) { #ifdef NO_GARBAGE_COLLECTOR assert(storedStatic._unVal.pRefCounted->_uiRef > 1); __Release(storedStatic._type, storedStatic._unVal); #else ss->_refs_table.Release(storedStatic); #endif } func->_staticmemos[staticIdx].Null(); instr[i].op = _OP_DATA_NOP; } } } } SQInteger SQFuncState::GetNumericConstant(const SQInteger cons) { return GetConstant(SQObjectPtr(cons)); } SQInteger SQFuncState::GetNumericConstant(const SQFloat cons) { return GetConstant(SQObjectPtr(cons)); } SQInteger SQFuncState::GetConstant(const SQObjectPtr &cons, int max_const_no) { SQObjectPtr val; max_const_no = max_const_no < MAX_LITERALS ? max_const_no : MAX_LITERALS; if(!_table(_literals)->Get(cons,val)) { if(_nliterals >= max_const_no) { if (max_const_no == MAX_LITERALS) { val.Null(); _ctx.reportDiagnostic(DiagnosticsId::DI_TOO_MANY_SYMBOLS, -1, -1, 0, "literals"); } else return -1; } val = _nliterals; _table(_literals)->NewSlot(cons,val); _nliterals++; } int iv = _integer(val); return iv <= max_const_no ? iv : -1; } void SQFuncState::SetInstructionParams(SQInteger pos,SQInteger arg0,SQInteger arg1,SQInteger arg2,SQInteger arg3) { _instructions[pos]._arg0=(unsigned char)*((SQUnsignedInteger *)&arg0); _instructions[pos]._arg1=(SQInt32)*((SQUnsignedInteger *)&arg1); _instructions[pos]._arg2=(unsigned char)*((SQUnsignedInteger *)&arg2); _instructions[pos]._arg3=(unsigned char)*((SQUnsignedInteger *)&arg3); } void SQFuncState::SetInstructionParam(SQInteger pos,SQInteger arg,SQInteger val) { switch(arg){ case 0:_instructions[pos]._arg0=(unsigned char)*((SQUnsignedInteger *)&val);break; case 1:case 4:_instructions[pos]._arg1=(SQInt32)*((SQUnsignedInteger *)&val);break; case 2:_instructions[pos]._arg2=(unsigned char)*((SQUnsignedInteger *)&val);break; case 3:_instructions[pos]._arg3=(unsigned char)*((SQUnsignedInteger *)&val);break; }; } SQInteger SQFuncState::AllocStackPos() { SQInteger npos=_vlocals.size(); if(npos >= MAX_FUNC_STACKSIZE) _ctx.reportDiagnostic(DiagnosticsId::DI_TOO_MANY_SYMBOLS, -1, -1, 0, "locals"); _vlocals.push_back(SQLocalVarInfo()); _vlocals_info.push_back(SQCompiletimeVarInfo{}); if(_vlocals.size()>((SQUnsignedInteger)_stacksize)) { _stacksize=_vlocals.size(); } return npos; } SQInteger SQFuncState::PushTarget(SQInteger n) { if(n!=-1){ _targetstack.push_back(n); return n; } n=AllocStackPos(); _targetstack.push_back(n); return n; } SQInteger SQFuncState::GetUpTarget(SQInteger n){ return _targetstack[((_targetstack.size()-1)-n)]; } SQInteger SQFuncState::TopTarget(){ return _targetstack.back(); } SQInteger SQFuncState::PopTarget() { SQUnsignedInteger npos=_targetstack.back(); assert(npos < _vlocals.size()); SQLocalVarInfo &t = _vlocals[npos]; if(sq_type(t._name)==OT_NULL){ _vlocals.pop_back(); _vlocals_info.pop_back(); } _targetstack.pop_back(); return npos; } SQInteger SQFuncState::GetStackSize() { return _vlocals.size(); } SQInteger SQFuncState::CountOuters(SQInteger stacksize) { SQInteger outers = 0; SQInteger k = _vlocals.size() - 1; while(k >= stacksize) { SQLocalVarInfo &lvi = _vlocals[k]; k--; if(lvi._end_op == UINT32_MINUS_ONE) { //this means is an outer outers++; } } return outers; } void SQFuncState::SetStackSize(SQInteger n) { SQInteger size=_vlocals.size(); while(size>n){ size--; SQLocalVarInfo lvi = _vlocals.back(); if(sq_type(lvi._name)!=OT_NULL){ if(lvi._end_op == UINT32_MINUS_ONE) { //this means is an outer _outers--; } lvi._end_op = GetCurrentPos(); _localvarinfos.push_back(lvi); } _vlocals.pop_back(); _vlocals_info.pop_back(); } } bool SQFuncState::IsLocal(SQUnsignedInteger stkpos) { if(stkpos>=_vlocals.size())return false; else if(sq_type(_vlocals[stkpos]._name)!=OT_NULL)return true; return false; } SQInteger SQFuncState::PushLocalVariable(const SQObject &name, const SQCompiletimeVarInfo& ct_var_info) { SQInteger pos=_vlocals.size(); SQLocalVarInfo lvi; lvi._name=name; lvi._start_op=GetCurrentPos()+1; lvi._pos=_vlocals.size(); lvi._varFlags=ct_var_info.var_flags; _vlocals.push_back(lvi); _vlocals_info.push_back(ct_var_info); if(_vlocals.size()>((SQUnsignedInteger)_stacksize))_stacksize=_vlocals.size(); return pos; } SQInteger SQFuncState::GetLocalVariable(const SQObject &name, SQCompiletimeVarInfo& ct_var_info) { SQInteger locals=_vlocals.size(); while(locals>=1){ SQLocalVarInfo &lvi = _vlocals[locals-1]; if(sq_type(lvi._name)==OT_STRING && _string(lvi._name)==_string(name)){ ct_var_info = _vlocals_info[locals-1]; return locals-1; } locals--; } return -1; } void SQFuncState::MarkLocalAsOuter(SQInteger pos) { SQLocalVarInfo &lvi = _vlocals[pos]; lvi._end_op = UINT32_MINUS_ONE; _outers++; } SQInteger SQFuncState::GetOuterVariable(const SQObject &name, SQCompiletimeVarInfo &varInfo) { SQInteger outers = _outervalues.size(); for(SQInteger i = 0; iGetLocalVariable(name, varInfo); if(pos == -1) { pos = _parent->GetOuterVariable(name, varInfo); if(pos != -1) { _outervalues.push_back(SQOuterVar(SQObjectPtr(name), SQObjectPtr(SQInteger(pos)), otOUTER, varInfo.var_flags)); //local _outervalues_info.push_back(varInfo); return _outervalues.size() - 1; } } else { _parent->MarkLocalAsOuter(pos); _outervalues.push_back(SQOuterVar(SQObjectPtr(name), SQObjectPtr(SQInteger(pos)), otLOCAL, varInfo.var_flags)); //local _outervalues_info.push_back(varInfo); return _outervalues.size() - 1; } } return -1; } void SQFuncState::AddParameter(const SQObject &name, SQUnsignedInteger32 type_mask) // TODO: initializer node { PushLocalVariable(name, SQCompiletimeVarInfo{VF_PARAM | VF_ASSIGNABLE, type_mask, nullptr}); _parameters.push_back(SQObjectPtr(name)); _param_type_masks.push_back(type_mask); } void SQFuncState::AddLineInfos(SQInteger line, bool is_dbg_step_point, bool force) { if(_lastline!=line || force){ if(_lastline!=line) { SQFullLineInfo li; li._op = (GetCurrentPos()+1); li._line_offset = line; li._is_dbg_step_point = is_dbg_step_point; _full_line_infos.push_back(li); if (is_dbg_step_point && _ctx.getVm()->_compile_line_hook) _ctx.getVm()->_compile_line_hook(_ctx.getVm(), _ctx.sourceName(), line); } _lastline=line; } } void SQFuncState::DiscardTarget() { SQInteger discardedtarget = PopTarget(); SQInteger size = _instructions.size(); if(size > 0 && _optimization){ SQInstruction &pi = _instructions[size-1];//previous instruction switch(pi.op) { case _OP_SETI:case _OP_SETK:case _OP_SET:case _OP_NEWSLOTK:case _OP_NEWSLOT:case _OP_SETOUTER:case _OP_CALL:case _OP_NULLCALL: if(pi._arg0 == discardedtarget) { pi._arg0 = 0xFF; } } } } void SQFuncState::AddInstruction(SQInstruction &i) { SQInteger size = _instructions.size(); if (size > 0 && _optimization && !(lang_features & LF_DISABLE_OPTIMIZER)){ //simple optimizer SQInstruction &pi = _instructions[size-1];//previous instruction switch(i.op) { case _OP_JZ: if( pi.op == _OP_CMP && pi._arg1 < 0xFF) { pi.op = _OP_JCMP; pi._arg0 = (unsigned char)pi._arg1; pi._arg3 |= (uint8_t(i._arg2 ? 1 : 0)<<3); pi._arg1 = i._arg1; return; } break; case _OP_SET: if(i._arg0 == i._arg3) { i._arg0 = 0xFF; } if( pi.op == _OP_LOAD && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0))){ // arg1 is size of int pi._arg2 = i._arg2; pi.op = _OP_SETK; pi._arg0 = i._arg0; pi._arg3 = i._arg3; return; } if( pi.op == _OP_LOADINT && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0))){ pi._arg2 = i._arg2; pi.op = _OP_SETI; // arg1 is size of int pi._arg0 = i._arg0; pi._arg3 = i._arg3; return; } break; case _OP_NEWSLOT: if(i._arg0 == i._arg3) { i._arg0 = 0xFF; } if( (pi.op == _OP_LOADINT || pi.op == _OP_LOAD) && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0))){ // arg1 is size of int pi._arg1 = pi.op == _OP_LOADINT ? GetNumericConstant((SQInteger)pi._arg1) : pi._arg1; pi._arg0 = i._arg0; pi._arg3 = i._arg3; pi._arg2 = i._arg2; pi.op = _OP_NEWSLOTK; return; } break; case _OP_SETI: case _OP_SETK: case _OP_NEWSLOTK: if(i._arg0 == i._arg3) { i._arg0 = 0xFF; } break; case _OP_SETOUTER: if(i._arg0 == i._arg2) { i._arg0 = 0xFF; } break; case _OP_RETURN: if( _parent && i._arg0 != MAX_FUNC_STACKSIZE && pi.op == _OP_CALL && _returnexp < size-1) { pi.op = _OP_TAILCALL; } else if(pi.op == _OP_CLOSE){ pi = i; return; } break; case _OP_GET: if( pi.op == _OP_LOAD && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0))){ // arg1 is size of int pi._arg2 = i._arg2; pi.op = _OP_GETK; pi._arg0 = i._arg0; pi._arg3 = i._arg3; return; } if( pi.op == _OP_LOADINT && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0))){ // arg1 is size of int // if (GetNumericConstant((SQInteger)pi._arg1) < 256) pi._arg2 = i._arg2; pi._arg1 = GetNumericConstant((SQInteger)pi._arg1); pi.op = _OP_GETK; pi._arg0 = i._arg0; pi._arg3 = i._arg3; return; } break; case _OP_PREPCALL: if( pi.op == _OP_LOAD && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0))){ pi.op = _OP_PREPCALLK; pi._arg0 = i._arg0; pi._arg2 = i._arg2; pi._arg3 = i._arg3; return; } break; case _OP_APPENDARRAY: { SQInteger aat = -1; switch(pi.op) { case _OP_LOAD: aat = AAT_LITERAL; break; case _OP_LOADINT: aat = AAT_INT; break; case _OP_LOADBOOL: aat = AAT_BOOL; break; case _OP_LOADFLOAT: aat = AAT_FLOAT; break; default: break; } if(aat != -1 && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0))){ pi.op = _OP_APPENDARRAY; pi._arg0 = i._arg0; pi._arg2 = (unsigned char)aat; pi._arg3 = MAX_FUNC_STACKSIZE; return; } } break; case _OP_MOVE: switch(pi.op) { case _OP_GET: case _OP_GETK: case _OP_ADD: case _OP_SUB: case _OP_MUL: case _OP_DIV: case _OP_MOD: case _OP_BITW: case _OP_LOADINT: case _OP_LOADFLOAT: case _OP_LOADBOOL: case _OP_LOAD: case _OP_NEG: case _OP_NOT: case _OP_BWNOT: case _OP_ADDI: case _OP_CMP: case _OP_TYPEOF: case _OP_CLONE: if(pi._arg0 == i._arg1 && !IsLocal(pi._arg0)) { pi._arg0 = i._arg0; _optimization = false; return; } } if(pi.op == _OP_GETOUTER && i._arg1 < 256 && i._arg0 && !pi._arg2) { pi._arg2 = i._arg0; pi._arg3 = (unsigned char)i._arg1; return; } if(pi.op == _OP_LOADCALLEE && i._arg1 < 256 && i._arg0 && !pi._arg2) { pi._arg2 = i._arg0; pi._arg3 = (unsigned char)i._arg1; return; } if(pi.op == _OP_MOVE && i._arg1 < 256) { pi.op = _OP_DMOVE; pi._arg2 = i._arg0; pi._arg3 = (unsigned char)i._arg1; return; } break; case _OP_LOAD: if(pi.op == _OP_LOAD && i._arg1 < 256) { pi.op = _OP_DLOAD; pi._arg2 = i._arg0; pi._arg3 = (unsigned char)i._arg1; return; } break; case _OP_EQ:case _OP_NE: if((pi.op == _OP_LOAD || pi.op == _OP_LOADINT || pi.op == _OP_LOADFLOAT || pi.op == _OP_LOADBOOL) && pi._arg0 == i._arg1 && (!IsLocal(pi._arg0) )) { if (pi.op != _OP_LOAD) { pi._arg1 = pi.op == _OP_LOADINT ? GetNumericConstant((SQInteger)pi._arg1) : pi.op == _OP_LOADFLOAT ? GetNumericConstant(pi._farg1) : GetConstant(SQObjectPtr(bool(pi._arg1))); } pi.op = i.op; pi._arg0 = i._arg0; pi._arg2 = i._arg2; pi._arg3 = 1; return; } break; case _OP_LOADNULLS: if((pi.op == _OP_LOADNULLS && pi._arg0+pi._arg1 == i._arg0)) { pi._arg1 = pi._arg1 + 1; pi.op = _OP_LOADNULLS; //-V1048 return; } break; } } _optimization = true; _instructions.push_back(i); } SQObjectPtr SQFuncState::CreateString(const char *s,SQInteger len) { return SQObjectPtr(SQString::Create(_sharedstate,s,len)); } void SQFuncState::CheckForPurity() { // Only set pure flag to true. // Don't reset it to false, don't assume that non-pure operations make the function impure. // Allow calls, arithmetics and other operations. int count = _instructions.size(); for (int i = 0; i < count; i += sq_opcode_length(_instructions[i].op)) { if (!sq_is_pure_op(_instructions[i].op)) { return; } } _purefunction = true; } SQFunctionProto *SQFuncState::BuildProto() { bool useCompressedLineInfos = true; int firstLine = INT_MAX; int lastLine = 0; for (SQUnsignedInteger ni = 0; ni < _full_line_infos.size(); ni++) { int line = _full_line_infos[ni]._line_offset; if (line < firstLine) firstLine = line; if (line > lastLine) lastLine = line; if (_full_line_infos[ni]._op > 255) useCompressedLineInfos = false; } if (lastLine - firstLine > 127) useCompressedLineInfos = false; SQFunctionProto *f=SQFunctionProto::Create(_ss,lang_features,_instructions.size(), _nliterals,_parameters.size(),_functions.size(),_outervalues.size(), _full_line_infos.size(),useCompressedLineInfos,_localvarinfos.size(),_defaultparams.size(), _staticmemos_count); SQObjectPtr refidx,key,val; SQInteger idx; f->_stacksize = _stacksize; f->_sourcename = _sourcename; f->_bgenerator = _bgenerator; f->_purefunction = _purefunction; f->_nodiscard = _nodiscard; f->_name = _name; f->_inside_hoisted_scope = _hoistLevel > 0; f->_result_type_mask = _result_type_mask; while((idx=_table(_literals)->Next(false,refidx,key,val))!=-1) { f->_literals[_integer(val)]=key; refidx=idx; } for(SQUnsignedInteger nf = 0; nf < _functions.size(); nf++) f->_functions[nf] = _functions[nf]; for(SQUnsignedInteger no = 0; no < _outervalues.size(); no++) f->_outervalues[no] = _outervalues[no]; for(SQUnsignedInteger nl = 0; nl < _localvarinfos.size(); nl++) f->_localvarinfos[nl] = _localvarinfos[nl]; for(SQUnsignedInteger np = 0; np < _parameters.size(); np++) { f->_parameters[np] = _parameters[np]; f->_param_type_masks[np] = _param_type_masks[np]; } f->_lineinfos->_is_compressed = useCompressedLineInfos; f->_lineinfos->_first_line = firstLine; if (useCompressedLineInfos) { for(SQUnsignedInteger ni = 0; ni < _full_line_infos.size(); ni++) { SQCompressedLineInfo *li = (SQCompressedLineInfo *)(void *)(f->_lineinfos + 1) + ni; li->_op = _full_line_infos[ni]._op; li->_line_offset = _full_line_infos[ni]._line_offset - (unsigned)firstLine; li->_is_dbg_step_point = _full_line_infos[ni]._is_dbg_step_point; } } else { for(SQUnsignedInteger ni = 0; ni < _full_line_infos.size(); ni++) { SQFullLineInfo *li = (SQFullLineInfo *)(void *)(f->_lineinfos + 1) + ni; *li = _full_line_infos[ni]; li->_line_offset -= firstLine; } } for(SQUnsignedInteger nd = 0; nd < _defaultparams.size(); nd++) f->_defaultparams[nd] = _defaultparams[nd]; memcpy(f->_instructions,&_instructions[0],_instructions.size()*sizeof(SQInstruction)); f->_varparams = _varparams; return f; } SQFuncState *SQFuncState::PushChildState(SQSharedState *ss) { SQFuncState *child = (SQFuncState *)sq_malloc(ss->_alloc_ctx, sizeof(SQFuncState)); new (child) SQFuncState(ss,this,_ctx); _childstates.push_back(child); return child; } void SQFuncState::PopChildState() { SQFuncState *child = _childstates.back(); SQAllocContext ctx = _ss->_alloc_ctx; sq_delete(ctx, child,SQFuncState); _childstates.pop_back(); } SQFuncState::~SQFuncState() { while(_childstates.size() > 0) { PopChildState(); } } #endif ================================================ FILE: squirrel/compiler/sqfuncstate.h ================================================ /* see copyright notice in squirrel.h */ #ifndef _SQFUNCSTATE_H_ #define _SQFUNCSTATE_H_ /////////////////////////////////// #include "squtils.h" #include "compilationcontext.h" namespace SQCompilation { class Expr; } using namespace SQCompilation; struct SQCompiletimeVarInfo { char var_flags; SQUnsignedInteger32 type_mask; Expr *initializer; SQCompiletimeVarInfo() { var_flags = 0; type_mask = ~0u; initializer = nullptr; } SQCompiletimeVarInfo(char var_flags, SQUnsignedInteger32 type_mask, Expr *initializer) : var_flags(var_flags), type_mask(type_mask), initializer(initializer) {} }; struct SQFuncState { SQFuncState(SQSharedState *ss,SQFuncState *parent,SQCompilationContext &ctx); ~SQFuncState(); SQFuncState *PushChildState(SQSharedState *ss); void PopChildState(); void AddInstruction(SQOpcode _op,SQInteger arg0=0,SQInteger arg1=0,SQInteger arg2=0,SQInteger arg3=0){SQInstruction i(_op,arg0,arg1,arg2,arg3);AddInstruction(i);} void AddInstruction(SQInstruction &i); void SetInstructionParams(SQInteger pos,SQInteger arg0,SQInteger arg1,SQInteger arg2=0,SQInteger arg3=0); void SetInstructionParam(SQInteger pos,SQInteger arg,SQInteger val); SQInstruction &GetInstruction(SQInteger pos){return _instructions[pos];} void PopInstructions(SQInteger size){for(SQInteger i=0;i _vlocals_info; // compile time only SQIntVec _targetstack; SQInteger _stacksize; bool _varparams; bool _bgenerator; bool _purefunction; bool _nodiscard; SQIntVec _unresolvedbreaks; SQIntVec _unresolvedcontinues; SQIntVec _expr_block_results; SQObjectPtrVec _functions; SQObjectPtrVec _parameters; sqvector _param_type_masks; SQUnsignedInteger32 _result_type_mask; SQOuterVarVec _outervalues; sqvector _outervalues_info; // compile time only SQInstructionVec _instructions; SQLocalVarInfoVec _localvarinfos; SQObjectPtr _literals; SQObjectPtr _name; SQObjectPtr _sourcename; SQInteger _nliterals; SQFullLineInfoVec _full_line_infos; SQFuncState *_parent; SQIntVec _breaktargets; SQIntVec _continuetargets; SQIntVec _blockstacksizes; SQIntVec _defaultparams; SQInteger _lastline; SQInteger _traps; //contains number of nested exception traps SQInteger _outers; SQInteger _hoistLevel; SQInteger _staticmemos_count; bool _optimization; SQSharedState *_sharedstate; sqvector _childstates; SQInteger GetConstant(const SQObjectPtr &cons, int max_const_no = 0x7FFFFFFF);//will return value <= max_const_no, or -1 private: SQCompilationContext &_ctx; SQSharedState *_ss; }; #endif //_SQFUNCSTATE_H_ ================================================ FILE: squirrel/compiler/sqio.cpp ================================================ #include "sqio.h" #include #include #include typedef union { uint64_t v; uint8_t b[sizeof(uint64_t)]; } U64Conv; uint64_t InputStream::readVaruint() { uint64_t v = 0; uint8_t t = 0; uint8_t s = 0; do { t = readByte(); v |= uint64_t(t & 0x7F) << s; s += 7; } while (t & 0x80); return v; } int64_t InputStream::readVarint() { union { int64_t i; uint64_t u; } conv; conv.u = readVaruint(); return conv.i; } int8_t InputStream::readInt8() { union { int8_t i; uint8_t u; } conv; conv.u = readByte(); return conv.i; } int16_t InputStream::readInt16() { return (int16_t)readVarint(); } int32_t InputStream::readInt32() { return (int32_t)readVarint(); } int64_t InputStream::readInt64() { return (int64_t)readVarint(); } intptr_t InputStream::readIntptr() { return readInt64(); } uint8_t InputStream::readUInt8() { return readByte(); } uint16_t InputStream::readUInt16() { return (uint16_t)readVaruint(); } uint32_t InputStream::readUInt32() { return (uint32_t)readVaruint(); } uint64_t InputStream::readUInt64() { return readVaruint(); } uintptr_t InputStream::readUIntptr() { return readUInt64(); } uint64_t InputStream::readRawUInt64() { U64Conv conv; for (int i = 0; i < sizeof conv.b; ++i) { conv.b[i] = readByte(); } return conv.v; } // --------------------------------------------- void OutputStream::writeVaruint(uint64_t v) { while (v > 0x7F) { writeByte(0x80 | uint8_t(v & 0x7F)); v >>= 7; } writeByte(uint8_t(v)); } void OutputStream::writeVarint(int64_t v) { union { int64_t i; uint64_t u; } conv; conv.i = v; writeVaruint(conv.u); } void OutputStream::writeInt8(int8_t v) { union { uint8_t u; int8_t i; } conv; conv.i = v; writeByte(conv.u); } void OutputStream::writeInt16(int16_t v) { writeVarint(v); } void OutputStream::writeInt32(int32_t v) { writeVarint(v); } void OutputStream::writeInt64(int64_t v) { writeVarint(v); } void OutputStream::writeIntptr(intptr_t v) { writeInt64(v); } void OutputStream::writeUInt8(uint8_t v) { writeByte(v); } void OutputStream::writeUInt16(uint16_t v) { writeVaruint(v); } void OutputStream::writeUInt32(uint32_t v) { writeVaruint(v); } void OutputStream::writeUInt64(uint64_t v) { writeVaruint(v); } void OutputStream::writeUIntptr(uintptr_t v) { writeUInt64(v); } void OutputStream::writeString(const char *s) { while (*s) { writeByte(*s++); } } void OutputStream::writeChar(char c) { writeInt8(c); } void OutputStream::writeRawUInt64(uint64_t v) { U64Conv conv; conv.v = v; for (int i = 0; i < sizeof conv.b; ++i) { writeByte(conv.b[i]); } } //------------------------------------------------------- uint8_t StdInputStream::readByte() { int v = i.get(); assert(v != EOF); return static_cast(v); } size_t StdInputStream::pos() { return static_cast(i.tellg()); } void StdInputStream::seek(size_t p) { i.seekg(static_cast(p)); } FileInputStream::FileInputStream(const char *fileName) { file = fopen(fileName, "rb"); } FileInputStream::~FileInputStream() { fclose(file); } uint8_t FileInputStream::readByte() { int v = fgetc(file); assert(v != EOF); return static_cast(v); } size_t FileInputStream::pos() { return static_cast(ftell(file)); } void FileInputStream::seek(size_t p) { fseek(file, p, SEEK_SET); } uint8_t MemoryInputStream::readByte() { assert(ptr < size); return buffer[ptr++]; } size_t MemoryInputStream::pos() { return ptr; } void MemoryInputStream::seek(size_t p) { assert(p <= size); ptr = p; } //----------------------------------------------------------- void StdOutputStream::writeByte(uint8_t v) { o.put(v); } size_t StdOutputStream::pos() { return static_cast(o.tellp()); } void StdOutputStream::seek(size_t p) { o.seekp(static_cast(p)); } FileOutputStream::FileOutputStream(const char *fileName) { file = fopen(fileName, "wb"); close = true; } FileOutputStream::~FileOutputStream() { if (close) { fclose(file); } } void FileOutputStream::writeByte(uint8_t v) { fputc(v & 0xFF, file); } size_t FileOutputStream::pos() { return static_cast(ftell(file)); } void FileOutputStream::seek(size_t p) { fseek(file, p, SEEK_SET); } MemoryOutputStream::~MemoryOutputStream() { if (_buffer) free(_buffer); } void MemoryOutputStream::resize(size_t n) { if (n < _size) return; uint8_t *newBuffer = (uint8_t*)malloc(n); assert(newBuffer); if (_buffer) { memcpy(newBuffer, _buffer, _size * sizeof(uint8_t)); //-V575 } free(_buffer); _buffer = newBuffer; _size = n; } void MemoryOutputStream::writeByte(uint8_t v) { if (ptr >= _size) { resize((_size + 512) << 2); } _buffer[ptr++] = v; } size_t MemoryOutputStream::pos() { return ptr; } void MemoryOutputStream::seek(size_t p) { assert(p <= _size); ptr = p; } ================================================ FILE: squirrel/compiler/sqtypeparser.cpp ================================================ #include "sqtypeparser.h" #include struct SQRawTypeDecl { const char * names[3]; // only [0] is valid, the rest are synonyms SQUnsignedInteger32 typeMask; }; static SQRawTypeDecl rawTypeDecls[] = { { { "bool", "boolean", NULL }, _RT_BOOL }, { { "number", "num", NULL }, (_RT_FLOAT | _RT_INTEGER) }, { { "int", "integer", NULL }, _RT_INTEGER }, { { "float", "double", "real" }, _RT_FLOAT }, { { "string", "str", NULL }, _RT_STRING }, { { "table", "dict", "map" }, _RT_TABLE }, { { "array", "list", "vector" }, _RT_ARRAY }, { { "userdata", "user", "object" }, _RT_USERDATA }, { { "function", "func", "closure" }, (_RT_CLOSURE | _RT_NATIVECLOSURE) }, { { "generator", "gen", "yield" }, _RT_GENERATOR }, { { "userpointer", "ptr", "pointer" }, _RT_USERPOINTER }, { { "thread", "coroutine", "fiber" }, _RT_THREAD }, { { "instance", "inst", "object" }, _RT_INSTANCE }, { { "class", NULL, NULL }, _RT_CLASS }, { { "weakref", "reference", "ref" }, _RT_WEAKREF }, { { "null", "nil", "none" }, _RT_NULL }, { { "any", NULL, NULL }, ~0u }, { { NULL, NULL, NULL }, 0 } }; static const char* skip_spaces(const char* s) { while (*s && sq_isspace(*s)) s++; return s; } static bool is_str_equal_ignore_case(const char* str1, const char* str2) { while (*str1 && *str2 && sq_tolower(*str1) == sq_tolower(*str2)) { str1++; str2++; } return *str1 == *str2; } static bool parse_identifier(SQVM* vm, const char*& s, SQObjectPtr& res) { const char* p = s; if (!sq_isalpha(*p) && *p != '_') return false; p++; while (sq_isalnum(*p) || *p == '_') p++; res = SQString::Create(_ss(vm), s, p - s); s = p; return true; } bool sq_type_string_to_mask(const char* type_name, SQUnsignedInteger32& mask, const char*& suggestion) { bool found = false; mask = 0; suggestion = nullptr; for (SQRawTypeDecl* decl = rawTypeDecls; decl->names[0]; decl++) { if (!strcmp(type_name, decl->names[0])) { mask |= decl->typeMask; found = true; break; } for (int syn = 0; syn < 3; syn++) if (decl->names[syn] && is_str_equal_ignore_case(type_name, decl->names[syn])) suggestion = decl->names[0]; } return found; } static bool parse_type_mask(SQVM* vm, const char*& s, SQUnsignedInteger32& mask, SQInteger& error_pos, SQObjectPtr& error_string) { mask = 0; const char* p = skip_spaces(s); bool hasBrackets = false; if (*p == '(') { hasBrackets = true; p++; p = skip_spaces(p); } for (;;) { SQObjectPtr typeName; if (!parse_identifier(vm, p, typeName)) { error_pos = p - s; error_string = SQString::Create(_ss(vm), "Expected type name"); return false; } const char* suggestion = nullptr; SQUnsignedInteger32 currentTypeMask = 0; bool found = sq_type_string_to_mask(_stringval(typeName), currentTypeMask, suggestion); if (!found) { error_pos = p - s; char buf[256]; if (suggestion) scsprintf(buf, 256, "Invalid type name '%s', did you mean '%s'?", _stringval(typeName), suggestion); else scsprintf(buf, 256, "Invalid type name '%s'", _stringval(typeName)); error_string = SQString::Create(_ss(vm), buf); return false; } mask |= currentTypeMask; p = skip_spaces(p); if (*p == '|') { p++; p = skip_spaces(p); } else { break; } } if (hasBrackets) { if (*p != ')') { error_pos = p - s; error_string = SQString::Create(_ss(vm), "Expected ')' after type list"); return false; } p++; p = skip_spaces(p); } s = p; return true; } bool sq_parse_function_type_string(SQVM* vm, const char* s, SQFunctionType& res, SQInteger& error_pos, SQObjectPtr& error_string) { if (!s || !*s) { error_pos = 1; error_string = SQString::Create(_ss(vm), "Empty function type string"); return false; } const char* p = skip_spaces(s); res.objectTypeMask = ~0u; res.returnTypeMask = _RT_NULL; res.requiredArgs = 0; res.ellipsisArgTypeMask = 0; res.argNames.clear(); res.argTypeMask.clear(); for (;;) { if (strncmp(p, "pure ", 5) == 0) { res.pure = true; p = skip_spaces(p + 5); } else if (strncmp(p, "nodiscard ", 10) == 0) { res.nodiscard = true; p = skip_spaces(p + 10); } else break; } if (*p == '(') { if (!parse_type_mask(vm, p, res.objectTypeMask, error_pos, error_string)) { error_pos += p - s + 1; return false; } p = skip_spaces(p); if (*p != '.') { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Expected '.' after object type"); return false; } p++; p = skip_spaces(p); if (!parse_identifier(vm, p, res.functionName)) { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Expected function name after '.'"); return false; } } else { SQObjectPtr identifier1; const char* p_initial = p; if (!parse_identifier(vm, p, identifier1)) { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Expected function name"); return false; } if (*p == '.') { p++; p = skip_spaces(p); if (!parse_identifier(vm, p, res.functionName)) { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Expected function name after '.'"); return false; } const char* typeStr = _stringval(identifier1); const char* typeStrPtr = typeStr; SQUnsignedInteger32 objectTypeMask; SQInteger error_pos_local; SQObjectPtr error_string_local; if (!parse_type_mask(vm, typeStrPtr, objectTypeMask, error_pos_local, error_string_local)) { error_pos = (p_initial - s) + error_pos_local; error_string = error_string_local; return false; } if (*typeStrPtr != '\0') { error_pos = (p_initial - s) + (typeStrPtr - typeStr); error_string = SQString::Create(_ss(vm), "Invalid object type"); return false; } res.objectTypeMask = objectTypeMask; } else { res.functionName = identifier1; } } p = skip_spaces(p); if (*p != '(') { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Expected '(' after function name"); return false; } p++; p = skip_spaces(p); bool insideString = false; bool insideOptional = false; bool optionalBlockFinished = false; bool argumentProcessed = false; while (*p != ')') { p = skip_spaces(p); if (*p == '\0') { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Unterminated argument list"); return false; } if (*p == '[') { if (insideOptional) { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Nested optional blocks are not allowed"); return false; } if (optionalBlockFinished) { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Optional block must be the last argument"); return false; } insideOptional = true; p++; p = skip_spaces(p); continue; } if (*p == ']') { if (!insideOptional) { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Unmatched ']'"); return false; } insideOptional = false; optionalBlockFinished = true; p++; p = skip_spaces(p); continue; } if (*p == ',') { if (!argumentProcessed) { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Argument expected before ','"); return false; } p++; p = skip_spaces(p); argumentProcessed = false; continue; } if (strncmp(p, "...", 3) == 0) { argumentProcessed = true; if (res.ellipsisArgTypeMask != 0) { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Multiple ellipsis arguments"); return false; } p += 3; p = skip_spaces(p); if (*p == ':') { p++; p = skip_spaces(p); if (!parse_type_mask(vm, p, res.ellipsisArgTypeMask, error_pos, error_string)) { error_pos += p - s + 1; return false; } } else { res.ellipsisArgTypeMask = ~0u; } p = skip_spaces(p); if (*p != ')' && *p != ']') { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Expected ')' after ellipsis argument"); return false; } continue; } if (optionalBlockFinished) { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Argument after optional block"); return false; } SQObjectPtr argName; if (!parse_identifier(vm, p, argName)) { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Expected argument name"); return false; } argumentProcessed = true; SQUnsignedInteger32 argTypeMask = ~0u; p = skip_spaces(p); if (*p == ':') { p++; p = skip_spaces(p); if (!parse_type_mask(vm, p, argTypeMask, error_pos, error_string)) { error_pos += p - s + 1; return false; } } else if (*p != ',' && *p != ')' && *p != ']' && *p != '[' && *p != '=') { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Expected ':' after argument name"); return false; } SQObjectPtr defaultValue; char stringOpener = '\0'; if (*p == '=') { p++; p = skip_spaces(p); char stack[40]; int stackIndex = 0; const char * startDefaultValue = p; while (*p != '\0') { if (insideString) { if (*p == stringOpener) { insideString = false; p++; continue; } else if (*p == '\\') { p++; if (*p == '\0') { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Unterminated string in default value"); return false; } } p++; continue; } else if (*p == '"' || *p == '\'') { insideString = true; stringOpener = *p; p++; continue; } if (stackIndex >= sizeof(stack) - 1) { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Default value too complex. Too many nested structures"); return false; } if (*p == '(') stack[stackIndex++] = '('; else if (*p == ')') { if (stackIndex == 0) break; // end of default value if (stack[stackIndex - 1] != '(') { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Unmatched ')' in default value"); return false; } stackIndex--; } else if (*p == '[') stack[stackIndex++] = '['; else if (*p == ']') { if (stackIndex == 0) break; // end of default value if (stack[stackIndex - 1] != '[') { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Unmatched ']' in default value"); return false; } stackIndex--; } else if (*p == '{') stack[stackIndex++] = '{'; else if (*p == '}') { if (stackIndex == 0 || stack[stackIndex - 1] != '{') { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Unmatched '}' in default value"); return false; } stackIndex--; } else if (*p == ',' && stackIndex == 0) { break; // end of default value } p++; p = skip_spaces(p); } if (stackIndex > 0) { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Unfinished default value, unmatched brackets"); return false; } if (*p == '\0') { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Unterminated function type string"); return false; } SQInteger defaultValueLength = p - startDefaultValue; if (defaultValueLength > 0) defaultValue = SQString::Create(_ss(vm), startDefaultValue, defaultValueLength); else { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Expected default value after '='"); return false; } } else // have no default value { if (!insideOptional && res.defaultValues.size() > 0 && !sq_isnull(res.defaultValues.back())) { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Default value expected after optional argument"); return false; } } res.argNames.push_back(argName); res.argTypeMask.push_back(argTypeMask); res.defaultValues.push_back(defaultValue); if (!insideOptional && sq_isnull(defaultValue)) { res.requiredArgs++; } p = skip_spaces(p); if (*p == ',') { p++; p = skip_spaces(p); } else if (*p != ')' && *p != ']' && *p != '[') { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Expected ',' or ')'"); return false; } } // *p == ')' if (insideOptional) { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Unmatched '['"); return false; } if (insideString) { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Unterminated string in function type"); return false; } p++; p = skip_spaces(p); if (*p == ':') { p++; p = skip_spaces(p); if (!parse_type_mask(vm, p, res.returnTypeMask, error_pos, error_string)) { error_pos += p - s + 1; return false; } } p = skip_spaces(p); if (*p != '\0') { error_pos = p - s + 1; error_string = SQString::Create(_ss(vm), "Unexpected characters after function type string"); return false; } return true; } void sq_stringify_type_mask(char* buffer, int buffer_length, SQUnsignedInteger32 mask) { assert(buffer_length > 128); if ((mask & _RT_CLOSURE) || (mask & _RT_NATIVECLOSURE)) mask |= _RT_CLOSURE | _RT_NATIVECLOSURE; char* p = buffer; char* end = buffer + buffer_length - 1; auto append = [&](const char* str) { while (*str && p < end) *p++ = *str++; }; if (mask == ~0u) { append("any"); *p = '\0'; return; } bool first = true; for (const SQRawTypeDecl* decl = rawTypeDecls; decl->names[0]; decl++) if ((mask & decl->typeMask) == decl->typeMask) { if (!first) append("|"); append(decl->names[0]); first = false; mask &= ~decl->typeMask; } *p = '\0'; } SQObjectPtr sq_stringify_function_type(SQVM* vm, const SQFunctionType& ft) { const SQInteger bufferSize = 2048; // 2KB buffer char buffer[bufferSize]; char* p = buffer; char* end = buffer + bufferSize - 1; if (sq_isnull(ft.functionName)) { SQObjectPtr tmp; return tmp; } auto append = [&](const char* str) { while (*str && p < end) { *p++ = *str++; } }; auto appendTypeMask = [&](SQUnsignedInteger32 typeMask) { if (typeMask == ~0u) { append("any"); return; } bool first = true; for (const SQRawTypeDecl* decl = rawTypeDecls; decl->names[0]; decl++) { if ((typeMask & decl->typeMask) == decl->typeMask) { if (!first) { append("|"); } append(decl->names[0]); first = false; typeMask &= ~decl->typeMask; } } }; if (ft.pure) append("pure "); if (ft.nodiscard) append("nodiscard "); if (ft.objectTypeMask != ~0u) { bool isComplexType = true; for (const SQRawTypeDecl* decl = rawTypeDecls; decl->names[0]; decl++) if (ft.objectTypeMask == decl->typeMask) { isComplexType = false; break; } if (isComplexType) append("("); appendTypeMask(ft.objectTypeMask); if (isComplexType) append(")"); append("."); } append(_stringval(ft.functionName)); append("("); for (SQInteger i = 0; i < ft.argNames.size(); i++) { if (i > 0) append(", "); if (i == ft.requiredArgs) append("["); append(_stringval(ft.argNames[i])); if (ft.argTypeMask[i] != ~0u) { append(": "); appendTypeMask(ft.argTypeMask[i]); } if (!sq_isnull(ft.defaultValues[i])) { append(" = "); if (sq_isstring(ft.defaultValues[i])) append(_stringval(ft.defaultValues[i])); else append(""); } } if (ft.argNames.size() > ft.requiredArgs) append("]"); if (ft.ellipsisArgTypeMask != 0) { if (ft.argNames.size() > 0) { append(", "); } append("..."); if (ft.ellipsisArgTypeMask != ~0u) { append(": "); appendTypeMask(ft.ellipsisArgTypeMask); } } append(")"); if (ft.returnTypeMask != _RT_NULL) { append(": "); appendTypeMask(ft.returnTypeMask); } *p = '\0'; return SQObjectPtr(SQString::Create(_ss(vm), buffer, p - buffer)); } ================================================ FILE: squirrel/compiler/sqtypeparser.h ================================================ #pragma once #include "sqpcheader.h" #include "sqvm.h" #include "sqstring.h" #include "squtils.h" struct SQFunctionType { SQObjectPtr functionName; SQUnsignedInteger32 returnTypeMask; SQUnsignedInteger32 objectTypeMask; sqvector argNames; sqvector argTypeMask; sqvector defaultValues; // null|string SQInteger requiredArgs; // Not including `this` SQUnsignedInteger32 ellipsisArgTypeMask; // 0 if no ellipsis bool pure; bool nodiscard; SQFunctionType(SQSharedState *ss) : argNames(ss->_alloc_ctx), defaultValues(ss->_alloc_ctx), argTypeMask(ss->_alloc_ctx) { returnTypeMask = ~0u; objectTypeMask = ~0u; requiredArgs = 0; ellipsisArgTypeMask = 0; pure = false; nodiscard = false; } }; bool sq_parse_function_type_string(SQVM* vm, const char* s, SQFunctionType& res, SQInteger& error_pos, SQObjectPtr& error_string); SQObjectPtr sq_stringify_function_type(SQVM* vm, const SQFunctionType& ft); bool sq_type_string_to_mask(const char* type_name, SQUnsignedInteger32& mask, const char*& suggestion); void sq_stringify_type_mask(char* buffer, int buffer_length, SQUnsignedInteger32 mask); ================================================ FILE: squirrel/compiler/static_analyzer/analyzer.cpp ================================================ #include "analyzer.h" #include #include "sqtable.h" #include "name_shadowing_checker.h" #include "operator_classification.h" #include "checker_visitor.h" #include "global_state.h" namespace SQCompilation { const char *symbolContextName(SymbolKind k) { switch (k) { case SK_EXCEPTION: return "exception"; case SK_FUNCTION: return "function"; case SK_CLASS: return "class"; case SK_TABLE: return "table"; case SK_VAR: return "variable"; case SK_BINDING: return "binding"; case SK_CONST: return "const"; case SK_ENUM: return "enum"; case SK_ENUM_CONST: return "enum const"; case SK_PARAM: return "parameter"; case SK_FOREACH: return "iteration variable"; case SK_EXTERNAL_BINDING: return "external binding"; case SK_IMPORT: return "import"; default: return ""; } } void FunctionInfo::joinModifiable(const FunctionInfo *other) { for (auto &m : other->modifiable) { if (owner == m.owner) continue; addModifiable(m.name, m.owner); } } void FunctionInfo::addModifiable(const char *name, const FunctionExpr *o) { for (auto &m : modifiable) { if (m.owner == o && strcmp(name, m.name) == 0) return; } modifiable.push_back({ o, name }); } //================================================================ StaticAnalyzer::StaticAnalyzer(SQCompilationContext &ctx) : _ctx(ctx) { } void StaticAnalyzer::reportGlobalNamesWarnings(HSQUIRRELVM vm) { auto errorFunc = _ss(vm)->_compilererrorhandler; if (!errorFunc) return; // 1. Check multiple declarations std::string message; for (auto it = declaredGlobals.begin(); it != declaredGlobals.end(); ++it) { const char *name = it->first.c_str(); const auto &declarations = it->second; if (declarations.size() == 1) continue; for (int32_t i = 0; i < declarations.size(); ++i) { const IdLocation &loc = declarations[i]; if (loc.diagSilenced) continue; message.clear(); SQCompilationContext::renderDiagnosticHeader(DiagnosticsId::DI_GLOBAL_NAME_REDEF, &message, name); errorFunc(vm, SEV_WARNING, message.c_str(), loc.filename, loc.line, loc.column, "\n"); } } // 2. Check undefined usages for (auto it = usedGlobals.begin(); it != usedGlobals.end(); ++it) { const std::string &id = it->first; bool isKnownBinding = knownBindings.find(id) != knownBindings.end(); if (isKnownBinding) continue; if (declaredGlobals.find(id) != declaredGlobals.end()) continue; const auto &usages = it->second; for (int32_t i = 0; i < usages.size(); ++i) { const IdLocation &loc = usages[i]; if (loc.diagSilenced) continue; message.clear(); SQCompilationContext::renderDiagnosticHeader(DiagnosticsId::DI_UNDEFINED_GLOBAL, &message, id.c_str()); errorFunc(vm, SEV_WARNING, message.c_str(), loc.filename, loc.line, loc.column, "\n"); } } } static bool isSpaceOrTab(char c) { return c == '\t' || c == ' '; } void StaticAnalyzer::checkTrailingWhitespaces(HSQUIRRELVM vm, const char *sourceName, const char *code, size_t codeSize) { Arena arena(_ss(vm)->_alloc_ctx, "tmp"); SQCompilationContext ctx(vm, &arena, sourceName, code, codeSize, nullptr, true); int32_t line = 1; int32_t column = 1; for (int32_t idx = 0; idx < codeSize - 1; ++idx, ++column) { if (isSpaceOrTab(code[idx])) { int next = code[idx + 1]; if (!next || next == '\n' || next == '\r') { ctx.reportDiagnostic(DiagnosticsId::DI_SPACE_AT_EOL, line, column - 1, 1); } } else if (code[idx] == '\n') { column = 0; line++; } } } void StaticAnalyzer::mergeKnownBindings(const HSQOBJECT *bindings) { if (bindings && sq_istable(*bindings)) { SQTable *table = _table(*bindings); SQInteger idx = 0; SQObjectPtr pos(idx), key, val; while ((idx = table->Next(false, pos, key, val)) >= 0) { if (sq_isstring(key)) { SQInteger len = _string(key)->_len; const char *s = _string(key)->_val; knownBindings.emplace(std::string(s, s+len)); } pos._unVal.nInteger = idx; } } } void StaticAnalyzer::runAnalysis(RootBlock *root, const HSQOBJECT *bindings) { mergeKnownBindings(bindings); CheckerVisitor(_ctx).analyze(root, bindings); NameShadowingChecker(_ctx, bindings).analyze(root); } } ================================================ FILE: squirrel/compiler/static_analyzer/analyzer.h ================================================ #pragma once #include "compiler/ast.h" #include "compiler/compilationcontext.h" namespace SQCompilation { class StaticAnalyzer { SQCompilationContext &_ctx; public: StaticAnalyzer(SQCompilationContext &ctx); void runAnalysis(RootBlock *r, const HSQOBJECT *bindings); static void mergeKnownBindings(const HSQOBJECT *bindings); static void reportGlobalNamesWarnings(HSQUIRRELVM vm); static void checkTrailingWhitespaces(HSQUIRRELVM vm, const char *sn, const char *code, size_t codeSize); }; } ================================================ FILE: squirrel/compiler/static_analyzer/analyzer_internal.h ================================================ #pragma once namespace SQCompilation { enum ReturnTypeBits { RT_NOTHING = 1 << 0, RT_NULL = 1 << 1, RT_BOOL = 1 << 2, RT_NUMBER = 1 << 3, RT_STRING = 1 << 4, RT_TABLE = 1 << 5, RT_ARRAY = 1 << 6, RT_CLOSURE = 1 << 7, RT_FUNCTION_CALL = 1 << 8, RT_UNRECOGNIZED = 1 << 9, RT_THROW = 1 << 10, RT_CLASS = 1 << 11, }; inline const char *enumFqn(Arena *arena, const char *enumName, const char *cname) { int32_t l1 = strlen(enumName); int32_t l2 = strlen(cname); int32_t l = l1 + 1 + l2 + 1; char *r = (char *)arena->allocate(l); snprintf(r, l, "%s.%s", enumName, cname); return r; } inline int32_t strhash(const char *s) { int32_t r = 0; while (*s) { r *= 31; r += *s; ++s; } return r; } struct StringHasher { int32_t operator()(const char *s) const { return strhash(s); } }; struct StringEqualer { int32_t operator()(const char *a, const char *b) const { return strcmp(a, b) == 0; } }; } ================================================ FILE: squirrel/compiler/static_analyzer/assign_seq_terminator.h ================================================ #pragma once #include "compiler/compilationcontext.h" #include "compiler/ast.h" #include "node_equal_checker.h" namespace SQCompilation { class AssignSeqTerminatorFinder : public Visitor { const Expr *assignee; bool foundUsage; bool foundInterruptor; NodeEqualChecker eqChecker; public: AssignSeqTerminatorFinder(const Expr *asg) : assignee(asg), foundUsage(false), foundInterruptor(false), eqChecker() {} void visitNode(Node *n) { if (!foundInterruptor && !foundUsage) Visitor::visitNode(n); } void visitCallExpr(CallExpr *c) { foundInterruptor = true; // consider call as potential usage } void visitExpr(Expr *e) { Visitor::visitExpr(e); if (eqChecker.check(assignee, e)) foundUsage = true; } void visitFunctionExpr(FunctionExpr *e) { /* skip */ } bool check(Node *tree) { tree->visit(this); return foundUsage || foundInterruptor; } }; } ================================================ FILE: squirrel/compiler/static_analyzer/ast_helpers.h ================================================ #pragma once namespace SQCompilation { inline const Expr *deparen(const Expr *e) { if (!e) return nullptr; if (e->op() == TO_PAREN) return deparen(static_cast(e)->argument()); return e; } inline const Expr *deparenStatic(const Expr *e) { if (!e) return nullptr; if (e->op() == TO_PAREN || e->op() == TO_STATIC_MEMO) return deparen(static_cast(e)->argument()); return e; } inline const Expr *skipUnary(const Expr *e) { if (!e) return nullptr; if (e->op() == TO_INC) { return skipUnary(static_cast(e)->argument()); } if (TO_NOT <= e->op() && e->op() <= TO_DELETE) { return skipUnary(static_cast(e)->argument()); } return e; } inline const Statement *lastNonEmpty(const Block *b, int32_t &effectiveSize) { const Statement *r = nullptr; effectiveSize = 0; for (auto stmt : b->statements()) { if (stmt->op() != TO_EMPTY) { r = stmt; effectiveSize += 1; } } return r; } inline const Statement *unwrapBody(const Statement *stmt) { if (!stmt) return nullptr; if (stmt->op() != TO_BLOCK) return stmt; auto &stmts = stmt->asBlock()->statements(); if (stmts.empty()) return nullptr; return unwrapBody(stmts.back()); } // in contrast to `unwrapBody(...)` above this function skips empty statements inline const Statement *unwrapBodyNonEmpty(const Statement *stmt) { if (stmt == nullptr) return stmt; if (stmt->op() != TO_BLOCK) return stmt; int32_t effectiveSize = 0; const Statement *last = lastNonEmpty(stmt->asBlock(), effectiveSize); if (effectiveSize == 0) return nullptr; return unwrapBodyNonEmpty(last); } inline const Statement *unwrapSingleBlock(const Statement *stmt) { if (!stmt) return nullptr; if (stmt->op() != TO_BLOCK) return stmt; int32_t effectiveSize = 0; const Statement *last = lastNonEmpty(stmt->asBlock(), effectiveSize); if (effectiveSize == 0) return nullptr; if (effectiveSize != 1) return stmt; return unwrapSingleBlock(last); } inline Expr *unwrapExprStatement(Statement *stmt) { return stmt->op() == TO_EXPR_STMT ? static_cast(stmt)->expression() : nullptr; } inline const FunctionExpr *extractFunction(const Node *n) { if (!n) return nullptr; if (n->op() == TO_FUNCTION) return static_cast(n); if (n->op() == TO_VAR) return extractFunction(n->asDeclaration()->asVarDecl()->initializer()); return nullptr; } inline const Expr *extractAssignedExpression(const Node *n) { if (n->op() == TO_ASSIGN || n->op() == TO_NEWSLOT) return static_cast(n)->rhs(); if (n->op() == TO_VAR) return static_cast(n)->initializer(); return nullptr; } inline const Id *extractReceiver(const Expr *e) { const Expr *last = e; for (;;) { e = deparenStatic(e); if (e->op() == TO_CALL) { // -V522 const CallExpr *c = e->asCallExpr(); e = c->callee(); if (c->isNullable()) last = c->callee(); } else if (e->isAccessExpr()) { // -V522 const AccessExpr *acc = e->asAccessExpr(); e = acc->receiver(); if (acc->isNullable()) last = acc->receiver(); } else { break; } } return last->op() == TO_ID ? last->asId() : nullptr; } inline bool isFallThroughStatement(const Statement *stmt) { TreeOp op = stmt->op(); return op != TO_RETURN && op != TO_THROW && op != TO_BREAK && op != TO_CONTINUE; } inline bool isFallThroughBranch(const Statement *stmt) { if (stmt->op() != TO_BLOCK) return isFallThroughStatement(stmt); const Block *blk = stmt->asBlock(); for (const Statement *s : blk->statements()) { if (!isFallThroughStatement(s)) return false; } return true; } } ================================================ FILE: squirrel/compiler/static_analyzer/breakable_scope.h ================================================ #pragma once #include "checker_visitor.h" namespace SQCompilation { struct VarScope; enum BreakableScopeKind { BSK_LOOP, BSK_SWITCH }; struct BreakableScope { BreakableScopeKind kind; BreakableScope *parent; union { const LoopStatement *loop; const SwitchStatement *swtch; } node; VarScope *loopScope; VarScope *exitScope; CheckerVisitor *visitor; BreakableScope(CheckerVisitor *v, const SwitchStatement *swtch) : visitor(v), kind(BSK_SWITCH), loopScope(nullptr), exitScope(nullptr), parent(v->breakScope) { node.swtch = swtch; v->breakScope = this; } BreakableScope(CheckerVisitor *v, const LoopStatement *loop, VarScope *ls, VarScope *es) : visitor(v), kind(BSK_LOOP), loopScope(ls), exitScope(es), parent(v->breakScope) { assert(ls/* && es*/); node.loop = loop; v->breakScope = this; } ~BreakableScope() { visitor->breakScope = parent; } }; } ================================================ FILE: squirrel/compiler/static_analyzer/checker_visitor.cpp ================================================ #include #include #include #include "checker_visitor.h" #include "node_complexity_counter.h" #include "node_diff_computer.h" #include "node_equal_checker.h" #include "function_ret_type_eval.h" #include "modification_checker.h" #include "loop_terminator_collector.h" #include "ast_helpers.h" #include "analyzer_internal.h" #include "operator_classification.h" #include "assign_seq_terminator.h" #include "var_scope.h" #include "value_ref.h" #include "symbol_info.h" #include "naming.h" #include "config.h" #include "global_state.h" #include "breakable_scope.h" #include "sqtable.h" #include "sqarray.h" #include "sqclass.h" #include "sqfuncproto.h" #include "sqclosure.h" #include namespace SQCompilation { static bool isBinaryArith(const Expr *expr) { return TO_OROR <= expr->op() && expr->op() <= TO_SUB; } static bool isAssignExpr(const Expr *expr) { return isAssignOp(expr->op()); } static bool looksLikeBooleanExpr(const Expr *e) { TreeOp op = e->op(); // -V522 if (isBooleanResultOperator(op)) return true; if (op == TO_LITERAL) { return e->asLiteral()->kind() == LK_BOOL; // -V522 } if (op == TO_NULLC || op == TO_ANDAND || op == TO_OROR) { // check for `x?.y {??, ||, &&} false` return looksLikeBooleanExpr(e->asBinExpr()->rhs()); } return false; } void CheckerVisitor::putIntoGlobalNamesMap(std::unordered_map> &map, enum DiagnosticsId diag, const char *name, const Node *d) { std::string sourcenameCache(_ctx.sourceName()); auto fnIt = fileNames.find(sourcenameCache); IdLocation loc; if (fnIt == fileNames.end()) { auto it2 = fileNames.insert(sourcenameCache); loc.filename = it2.first->c_str(); } else { loc.filename = fnIt->c_str(); } loc.line = d->lineStart(); loc.column = d->columnStart(); loc.diagSilenced = _ctx.isDisabled(diag, loc.line, loc.column); std::string key(name); auto it = map.find(key); if (it != map.end()) { it->second.push_back(loc); } else { auto it2 = map.insert({ key, std::vector() }); it2.first->second.push_back(loc); } } void CheckerVisitor::storeGlobalDeclaration(const char *name, const Node *d) { putIntoGlobalNamesMap(declaredGlobals, DiagnosticsId::DI_GLOBAL_NAME_REDEF, name, d); } void CheckerVisitor::storeGlobalUsage(const char *name, const Node *d) { putIntoGlobalNamesMap(usedGlobals, DiagnosticsId::DI_UNDEFINED_GLOBAL, name, d); } CheckerVisitor::~CheckerVisitor() { for (auto &p : functionInfoMap) { if (p.second) p.second->~FunctionInfo(); } for (auto ev : externalValues) ev->~ExternalValueExpr(); } void CheckerVisitor::visitNode(Node *n) { nodeStack.push_back({ SST_NODE, n }); Visitor::visitNode(n); nodeStack.pop_back(); } void CheckerVisitor::report(const Node *n, int32_t id, ...) { if (isEffectsGatheringPass) return; va_list vargs; va_start(vargs, id); _ctx.vreportDiagnostic((enum DiagnosticsId)id, n->lineStart(), n->columnStart(), n->textWidth(), vargs); // -V522 va_end(vargs); } void CheckerVisitor::reportImportSlot(int line, int column, const char *name) { if (isEffectsGatheringPass) return; int width = (int)strlen(name); _ctx.reportDiagnostic(DiagnosticsId::DI_IMPORTED_NEVER_USED, line, column, width, name); } void CheckerVisitor::checkIdUsed(const Id *id, const Node *p, ValueRef *v) { const Expr *e = nullptr; if (p) { if (p->op() == TO_EXPR_STMT) e = static_cast(p)->expression(); else if (p->isExpression()) e = p->asExpression(); } bool assigned = v->assigned; if (e && isAssignExpr(e)) { const BinExpr *bin = static_cast(e); const Expr *lhs = bin->lhs(); Expr *rhs = bin->rhs(); bool simpleAsgn = e->op() == TO_ASSIGN; if (id == lhs) { bool used = v->info->usedAfterAssign || existsInTree(id, rhs); v->info->used |= used; v->assigned = true; v->lastAssigneeScope = currentScope; v->info->usedAfterAssign = false; if (simpleAsgn) return; } } // it's usage v->info->used = true; if (assigned) { v->info->usedAfterAssign = true; v->assigned = e ? TO_PLUSEQ <= e->op() && e->op() <= TO_MODEQ : false; } } void CheckerVisitor::checkAccessFromStatic(const GetFieldExpr *acc) { if (isEffectsGatheringPass) return; const Expr *r = acc->receiver(); if (r->op() != TO_ID || strcmp(r->asId()->name(), "this") != 0) return; const TableMember *m = nullptr; const ClassExpr *klass = nullptr; for (auto it = nodeStack.rbegin(); it != nodeStack.rend(); ++it) { if (it->sst == SST_TABLE_MEMBER) { m = it->member; ++it; if (it != nodeStack.rend() && it->sst == SST_NODE && it->n->op() == TO_CLASS) { klass = static_cast(it->n); } break; } } if (!m || !m->isStatic() || !klass) return; const auto &members = klass->members(); const char *memberName = acc->fieldName(); for (const auto &m : members) { if (m.key->op() == TO_LITERAL && m.key->asLiteral()->kind() == LK_STRING) { const char *klassMemberName = m.key->asLiteral()->s(); if (strcmp(memberName, klassMemberName) == 0) { if (!m.isStatic()) report(acc, DiagnosticsId::DI_USED_FROM_STATIC, memberName, "static member"); return; } } } report(acc, DiagnosticsId::DI_USED_FROM_STATIC, memberName, "static member"); } bool CheckerVisitor::hasDynamicContent(const SQObject &container) { if (!sq_istable(container)) return false; SQObjectPtr key(_ctx.getVm(), "__dynamic_content__"); SQObjectPtr val; return _table(container)->Get(key, val); } void CheckerVisitor::checkExternalField(const GetFieldExpr *acc) { if (isEffectsGatheringPass) return; const Expr *r = acc->receiver(); r = maybeEval(r, true); if (r->op() != TO_EXTERNAL_VALUE) return; const auto &container = r->asExternal()->value(); if (sq_isinstance(container)) return; SQObjectPtr key(_ctx.getVm(), acc->fieldName()); SQObject rawVal; if (!SQ_SUCCEEDED(sq_obj_get(_ctx.getVm(), &container, &key, &rawVal, false))) { if (!acc->isNullable() && !hasDynamicContent(container)) { report(acc, DI_MISSING_FIELD, acc->fieldName(), GetTypeName(container)); char buf[128]; snprintf(buf, sizeof(buf), "source of %s", GetTypeName(container)); report(r, DI_SEE_OTHER, buf); } return; } sq_poptop(_ctx.getVm()); // pop the stack copy pushed by sq_obj_get astValues[acc] = addExternalValue(rawVal, acc); } static bool cannotBeNull(const Expr *e) { switch (e->op()) { case TO_INC: case TO_NEG: case TO_NOT: case TO_BNOT: case TO_3CMP: case TO_XOR: case TO_OR: case TO_AND: case TO_EQ: case TO_NE: case TO_LE: case TO_LT: case TO_GE: case TO_GT: case TO_IN: case TO_INSTANCEOF: case TO_USHR: case TO_SHL: case TO_SHR: case TO_ADD: case TO_SUB: case TO_MUL: case TO_DIV: /*case TO_MOD:*/ case TO_TYPEOF: case TO_RESUME: case TO_BASE: case TO_ROOT_TABLE_ACCESS: case TO_ARRAY: case TO_TABLE: case TO_CLASS: case TO_FUNCTION: return true; case TO_LITERAL: return e->asLiteral()->kind() != LK_NULL; default: return false; } } void CheckerVisitor::reportIfCannotBeNull(const Expr *checkee, const Expr *n, const char *loc) { assert(n); if (checkee->op() == TO_NULLC) { checkee = maybeEval(static_cast(checkee)->rhs()); } if (checkee->op() == TO_TERNARY) { const TerExpr *ter = static_cast(checkee); const Expr *ifTrue = maybeEval(ter->b()); const Expr *ifFalse = maybeEval(ter->c()); if (cannotBeNull(ifTrue) && cannotBeNull(ifFalse)) { report(n, DiagnosticsId::DI_EXPR_NOT_NULL, loc); } return; } if (cannotBeNull(checkee)) report(n, DiagnosticsId::DI_EXPR_NOT_NULL, loc); } void CheckerVisitor::reportModifyIfContainer(const Expr *e, const Expr *mod) { bool found = false; const ForeachStatement *f = nullptr; for (auto it = nodeStack.rbegin(); it != nodeStack.rend(); ++it) { if (it->sst != SST_NODE) continue; const Node *n = it->n; if (n->op() == TO_FOREACH) { f = static_cast(n); if (_equalChecker.check(f->container(), e)) { found = true; break; } } } if (!found) return; for (auto it = nodeStack.rbegin(); it != nodeStack.rend(); ++it) { if (it->sst != SST_NODE) continue; if (it->n == f) break; const Node *n = it->n; if (n->op() == TO_BLOCK) { const auto &stmts = static_cast(n)->statements(); assert(stmts.size() > 0); const Statement *stmt = stmts.back(); if (stmt->op() == TO_RETURN || stmt->op() == TO_THROW || stmt->op() == TO_BREAK) return; } } report(mod, DiagnosticsId::DI_MODIFIED_CONTAINER); } static bool stringLooksLikeFormatTemplate(const char *s) { const char *bracePtr = strchr(s, '{'); if (bracePtr && (sq_isalpha(bracePtr[1]) || bracePtr[1] == '_')) { // check for strings specific to Dagor DataBlock objects bool isDagorBlk = (strstr(s, ":i=") || strstr(s, ":r=") || strstr(s, ":t=") || strstr(s, ":p2=") || strstr(s, ":p3=") || strstr(s, ":tm=")); return !isDagorBlk && bracePtr[1] && strchr(bracePtr + 2, '}'); } return false; } void CheckerVisitor::checkForgotSubst(const LiteralExpr *l) { if (isEffectsGatheringPass) return; if (l->kind() != LK_STRING) return; const CallExpr *candidate = nullptr; for (auto it = nodeStack.rbegin(); it != nodeStack.rend(); ++it) { if (it->sst != SST_NODE) continue; const Node *n = it->n; if (n->op() == TO_CALL) { candidate = static_cast(n); break; } } const char *s = l->s(); if (!stringLooksLikeFormatTemplate(s)) { return; } bool ok = false; if (candidate) { const Expr *callee = deparenStatic(candidate->callee()); const auto &arguments = candidate->arguments(); if (callee->op() == TO_GETFIELD) { // -V522 const GetFieldExpr *f = callee->asGetField(); const char *funcName = f->fieldName(); if (deparenStatic(f->receiver()) == l) { ok = strcmp(funcName, "subst") == 0; } else if (strcmp(funcName, "split") == 0) { ok = arguments.size() >= 1 && deparenStatic(arguments[0]) == l;; } } else if (callee->op() == TO_ID) { // -V522 if (strcmp("loc", callee->asId()->name()) == 0) { ok = arguments.size() >= 2 && deparenStatic(arguments[1]) == l; } } } if (!ok) report(l, DiagnosticsId::DI_FORGOT_SUBST); } void CheckerVisitor::checkContainerModification(const UnExpr *u) { if (isEffectsGatheringPass) return; if (u->op() != TO_DELETE) return; const Expr *arg = u->argument(); if (!arg->isAccessExpr()) return; const Expr *receiver = arg->asAccessExpr()->receiver(); reportModifyIfContainer(receiver, u); } void CheckerVisitor::checkAndOrPriority(const BinExpr *expr) { if (isEffectsGatheringPass) return; const Expr *l = expr->lhs(); const Expr *r = expr->rhs(); if (expr->op() == TO_OROR) { if (l->op() == TO_ANDAND || r->op() == TO_ANDAND) { report(expr, DiagnosticsId::DI_AND_OR_PAREN); } } } void CheckerVisitor::checkBitwiseParenBool(const BinExpr *expr) { if (isEffectsGatheringPass) return; const Expr *l = expr->lhs(); const Expr *r = expr->rhs(); if (expr->op() == TO_OROR || expr->op() == TO_ANDAND) { if (l->op() == TO_AND || l->op() == TO_OR || r->op() == TO_AND || r->op() == TO_OR) { report(expr, DiagnosticsId::DI_BITWISE_BOOL_PAREN); } } } void CheckerVisitor::checkNullCoalescingPriority(const BinExpr *expr) { if (isEffectsGatheringPass) return; const Expr *l = expr->lhs(); const Expr *r = expr->rhs(); if (expr->op() == TO_NULLC) { if (isSuspiciousNeighborOfNullCoalescing(l->op())) { report(l, DiagnosticsId::DI_NULL_COALESCING_PRIORITY, treeopStr(l->op())); } if (isSuspiciousNeighborOfNullCoalescing(r->op())) { report(r, DiagnosticsId::DI_NULL_COALESCING_PRIORITY, treeopStr(r->op())); } } } void CheckerVisitor::checkAssignmentToItself(const BinExpr *expr) { if (isEffectsGatheringPass) return; const Expr *l = expr->lhs(); const Expr *r = expr->rhs(); if (expr->op() == TO_ASSIGN) { auto *ll = deparen(l); auto *rr = deparen(r); if (_equalChecker.check(ll, rr)) { report(expr, DiagnosticsId::DI_ASG_TO_ITSELF); } } } void CheckerVisitor::checkParamAssignInLambda(const BinExpr *expr) { if (isEffectsGatheringPass) return; if (expr->op() != TO_ASSIGN) return; const Expr *lhs = expr->lhs(); if (lhs->op() != TO_ID) return; if (!currentInfo || !currentInfo->declaration) return; const FunctionExpr *func = currentInfo->declaration; // Only warn in expression lambdas (@(x) expr), not in block-body functions if (!func->isLambda()) return; const char *name = lhs->asId()->name(); ValueRef *v = findValueInScopes(name); if (!v || !v->info || v->info->kind != SK_PARAM) return; // Only warn if the parameter belongs to the current (innermost) function if (v->info->ownedScope->owner != func) return; report(expr, DiagnosticsId::DI_PARAM_ASSIGNMENT_IN_LAMBDA, name); } void CheckerVisitor::checkSameOperands(const BinExpr *expr) { if (isEffectsGatheringPass) return; const Expr *l = expr->lhs(); const Expr *r = expr->rhs(); if (isSuspiciousSameOperandsBinaryOp(expr->op())) { const Expr *ll = deparen(l); const Expr *rr = deparen(r); if (_equalChecker.check(ll, rr)) { if (ll->op() != TO_LITERAL || (ll->asLiteral()->kind() != LK_FLOAT && ll->asLiteral()->kind() != LK_INT)) { // -V522 report(expr, DiagnosticsId::DI_SAME_OPERANDS, treeopStr(expr->op())); } } } } void CheckerVisitor::checkAlwaysTrueOrFalse(const Expr *n) { if (isEffectsGatheringPass) return; const Node *cond = n; // maybeEval(n); if (cond->op() == TO_LITERAL) { const LiteralExpr *l = cond->asExpression()->asLiteral(); report(n, DiagnosticsId::DI_ALWAYS_T_OR_F, l->raw() ? "true" : "false"); } else if (cond->op() == TO_ARRAY || cond->op() == TO_TABLE || cond->op() == TO_CLASS || cond->op() == TO_FUNCTION || cond->isDeclaration()) { report(n, DiagnosticsId::DI_ALWAYS_T_OR_F, "true"); } } void CheckerVisitor::checkAlwaysTrueOrFalse(const TerExpr *expr) { checkAlwaysTrueOrFalse(expr->a()); } void CheckerVisitor::checkTernaryPriority(const TerExpr *expr) { if (isEffectsGatheringPass) return; const Expr *cond = expr->a(); if (isSuspiciousTernaryConditionOp(cond->op())) { const BinExpr *binCond = static_cast(cond); const Expr *l = binCond->lhs(); const Expr *r = binCond->rhs(); bool isIgnore = cond->op() == TO_AND && (isUpperCaseIdentifier(l) || isUpperCaseIdentifier(r) || (r->op() == TO_LITERAL && r->asLiteral()->kind() == LK_INT)); if (!isIgnore) { report(cond, DiagnosticsId::DI_TERNARY_PRIOR, treeopStr(cond->op())); } } } void CheckerVisitor::checkSameValues(const TerExpr *expr) { if (isEffectsGatheringPass) return; const Expr *ifTrue = expr->b(); const Expr *ifFalse = expr->c(); if (_equalChecker.check(ifTrue, ifFalse)) { report(expr, DiagnosticsId::DI_TERNARY_SAME_VALUES); } } void CheckerVisitor::checkCanBeSimplified(const TerExpr *expr) { if (isEffectsGatheringPass) return; const Expr *cond = expr->a(); const Expr *checkee = nullptr; if (cond->op() == TO_NE) { const BinExpr *b = static_cast(cond); if (b->lhs()->op() == TO_LITERAL && b->lhs()->asLiteral()->kind() == LK_NULL) checkee = b->rhs(); else if (b->rhs()->op() == TO_LITERAL && b->rhs()->asLiteral()->kind() == LK_NULL) checkee = b->lhs(); } if (!checkee) return; if (expr->c()->op() != TO_LITERAL || expr->c()->asLiteral()->kind() != LK_NULL) return; checkee = maybeEval(checkee); const Expr *t = maybeEval(expr->b()); if (_equalChecker.check(checkee, t)) { report(expr, DiagnosticsId::DI_CAN_BE_SIMPLIFIED); } } void CheckerVisitor::checkAlwaysTrueOrFalse(const BinExpr *bin) { if (isEffectsGatheringPass) return; TreeOp op = bin->op(); if (op != TO_ANDAND && op != TO_OROR) return; const Expr *lhs = bin->lhs(); const Expr *rhs = bin->rhs(); TreeOp cmpOp = lhs->op(); if (cmpOp == rhs->op() && (cmpOp == TO_NE || cmpOp == TO_EQ)) { const char *constValue = nullptr; if (op == TO_ANDAND && cmpOp == TO_EQ) constValue = "false"; if (op == TO_OROR && cmpOp == TO_NE) constValue = "true"; if (!constValue) return; const BinExpr *lhsBin = static_cast(lhs); const BinExpr *rhsBin = static_cast(rhs); const Expr *lconst = lhsBin->rhs(); const Expr *rconst = rhsBin->rhs(); if (lconst->op() == TO_LITERAL || isUpperCaseIdentifier(lconst)) { if (rconst->op() == TO_LITERAL || isUpperCaseIdentifier(rconst)) { if (_equalChecker.check(lhsBin->lhs(), rhsBin->lhs())) { if (!_equalChecker.check(lconst, rconst)) { report(bin, DiagnosticsId::DI_ALWAYS_T_OR_F, constValue); } } } } } else if ((lhs->op() == TO_NOT || rhs->op() == TO_NOT) && (lhs->op() != rhs->op())) { const char *v = op == TO_OROR ? "true" : "false"; if (lhs->op() == TO_NOT) { const UnExpr *u = static_cast(lhs); if (_equalChecker.check(u->argument(), rhs)) { report(bin, DiagnosticsId::DI_ALWAYS_T_OR_F, v); } } if (rhs->op() == TO_NOT) { const UnExpr *u = static_cast(rhs); if (_equalChecker.check(lhs, u->argument())) { report(bin, DiagnosticsId::DI_ALWAYS_T_OR_F, v); } } } } void CheckerVisitor::checkDeclarationInArith(const BinExpr *bin) { if (isEffectsGatheringPass) return; if (isArithOperator(bin->op())) { const Expr *lhs = maybeEval(bin->lhs()); const Expr *rhs = maybeEval(bin->rhs()); if (lhs->op() == TO_TABLE || lhs->op() == TO_CLASS || lhs->op() == TO_FUNCTION || lhs->op() == TO_ARRAY) { report(bin->lhs(), DiagnosticsId::DI_DECL_IN_EXPR); } if (bin->op() != TO_OROR && bin->op() != TO_ANDAND) { if (rhs->op() == TO_TABLE || rhs->op() == TO_CLASS || rhs->op() == TO_FUNCTION || rhs->op() == TO_ARRAY) { report(bin->rhs(), DiagnosticsId::DI_DECL_IN_EXPR); } } } } void CheckerVisitor::checkIntDivByZero(const BinExpr *bin) { if (isEffectsGatheringPass) return; if (isDivOperator(bin->op())) { const Expr *divided = maybeEval(bin->lhs()); const Expr *divisor = maybeEval(bin->rhs()); if (divisor->op() == TO_LITERAL) { const LiteralExpr *rhs = divisor->asLiteral(); if (rhs->raw() == 0) { report(bin, DiagnosticsId::DI_DIV_BY_ZERO); return; } if (divided->op() == TO_LITERAL) { const LiteralExpr *lhs = divided->asLiteral(); if (lhs->kind() == LK_INT && rhs->kind() == LK_INT) { if (rhs->i() == -1 && lhs->i() == MIN_SQ_INTEGER) { report(bin, DiagnosticsId::DI_INTEGER_OVERFLOW); } else if (lhs->i() % rhs->i() != 0) { report(bin, DiagnosticsId::DI_ROUND_TO_INT); } } } } } } void CheckerVisitor::checkPotentiallyNullableOperands(const BinExpr *bin) { if (isEffectsGatheringPass) return; TreeOp op = bin->op(); bool isRelOp = isBoolRelationOperator(op); bool isArithOp = isPureArithOperator(op); bool isAssign = op == TO_ASSIGN || op == TO_NEWSLOT; if (!isRelOp && !isArithOp && !isAssign) return; const Expr *lhs = bin->lhs(); const Expr *rhs = bin->rhs(); const char *opType = isRelOp ? "Comparison operation" : "Arithmetic operation"; bool lhsNullable = isPotentiallyNullable(lhs); bool rhsNullable = isPotentiallyNullable(rhs); if (!lhsNullable && !rhsNullable) return; // string concat with nullable is OK if (couldBeString(lhs) || couldBeString(rhs)) return; if (lhsNullable) { if (isAssign) { if (lhs->op() != TO_ID) { // -V522 report(bin->lhs(), DiagnosticsId::DI_NULLABLE_ASSIGNMENT); } } else { report(bin->lhs(), DiagnosticsId::DI_NULLABLE_OPERANDS, opType); } } if (rhsNullable && !isAssign) { report(bin->rhs(), DiagnosticsId::DI_NULLABLE_OPERANDS, opType); } } void CheckerVisitor::checkBitwiseToBool(const BinExpr *bin) { if (isEffectsGatheringPass) return; if (!isBitwiseOperator(bin->op())) return; const Expr *lhs = maybeEval(bin->lhs()); const Expr *rhs = maybeEval(bin->rhs()); if (looksLikeBooleanExpr(lhs) || looksLikeBooleanExpr(rhs)) { report(bin, DiagnosticsId::DI_BITWISE_OP_TO_BOOL); } } void CheckerVisitor::checkRelativeCompareWithBool(const BinExpr *expr) { if (isEffectsGatheringPass) return; if (!isBoolRelationOperator(expr->op())) return; const Expr *l = expr->lhs(); const Expr *r = expr->rhs(); const Expr *dl = deparenStatic(l); const Expr *dr = deparenStatic(r); const Expr *el = maybeEval(dl); const Expr *er = maybeEval(dr); bool l_b = looksLikeBooleanExpr(l); bool dl_b = looksLikeBooleanExpr(dl); bool el_b = looksLikeBooleanExpr(el); bool r_b = looksLikeBooleanExpr(r); bool dr_b = looksLikeBooleanExpr(dr); bool er_b = looksLikeBooleanExpr(er); bool left_cb = l_b || dl_b || el_b; bool right_cb = r_b || dr_b || er_b; if (left_cb != right_cb) { // -V522 report(expr, DiagnosticsId::DI_RELATIVE_CMP_BOOL); } } void CheckerVisitor::checkCompareWithBool(const BinExpr *expr) { if (isEffectsGatheringPass) return; const Expr *l = expr->lhs(); const Expr *r = expr->rhs(); TreeOp thisOp = expr->op(); TreeOp lhsOp = l->op(); TreeOp rhsOp = r->op(); // if (a == b != c) if (thisOp == TO_EQ || thisOp == TO_NE) { if (lhsOp == TO_EQ || lhsOp == TO_NE || rhsOp == TO_EQ || rhsOp == TO_NE) { report(expr, DiagnosticsId::DI_EQ_PAREN_MISSED); return; } } if (isBoolCompareOperator(thisOp) && lhsOp != TO_PAREN && rhsOp != TO_PAREN) { const Expr *lhs = maybeEval(l); const Expr *rhs = maybeEval(r); if (isBoolCompareOperator(lhs->op()) || isBoolCompareOperator(rhs->op())) { bool warn = true; if (expr->op() == TO_EQ || expr->op() == TO_NE) { //function_should_return_bool_prefix if (nameLooksLikeResultMustBeBoolean(findFieldName(l)) || nameLooksLikeResultMustBeBoolean(findFieldName(r)) || nameLooksLikeResultMustBeBoolean(findFieldName(rhs)) || nameLooksLikeResultMustBeBoolean(findFieldName(lhs))) { warn = false; } if (l->op() == TO_ID && r->op() == TO_ID) { warn = false; } } if (warn) { report(expr, DiagnosticsId::DI_COMPARE_WITH_BOOL); } } } } void CheckerVisitor::checkCopyOfExpression(const BinExpr *bin) { if (isEffectsGatheringPass) return; TreeOp op = bin->op(); if (op != TO_OROR && op != TO_ANDAND && op != TO_OR) return; const Expr *lhs = bin->lhs(); const Expr *cmp = bin->rhs(); while (cmp->op() == op) { const BinExpr *binCmp = static_cast(cmp); if (_equalChecker.check(lhs, binCmp->lhs()) || _equalChecker.check(lhs, binCmp->rhs())) { report(binCmp, DiagnosticsId::DI_COPY_OF_EXPR); } cmp = binCmp->rhs(); } } void CheckerVisitor::checkConstInBoolExpr(const BinExpr *bin) { if (isEffectsGatheringPass) return; if (bin->op() != TO_OROR && bin->op() != TO_ANDAND) return; const Expr *lhs = bin->lhs(); const Expr *rhs = bin->rhs(); bool leftIsConst = lhs->op() == TO_LITERAL || lhs->op() == TO_TABLE || lhs->op() == TO_CLASS || lhs->op() == TO_FUNCTION || lhs->op() == TO_ARRAY || isUpperCaseIdentifier(lhs); //-V522 bool rightIsConst = rhs->op() == TO_LITERAL || rhs->op() == TO_TABLE || rhs->op() == TO_CLASS || rhs->op() == TO_FUNCTION || rhs->op() == TO_ARRAY || isUpperCaseIdentifier(rhs); // -V522 /* if (rightIsConst && bin->op() == TO_OROR) { if (rhs->op() != TO_LITERAL || rhs->asLiteral()->kind() != LK_BOOL || rhs->asLiteral()->b() != true) { rightIsConst = false; } } */ if (leftIsConst || rightIsConst) { report(bin, DiagnosticsId::DI_CONST_IN_BOOL_EXPR); } } void CheckerVisitor::checkShiftPriority(const BinExpr *bin) { if (isEffectsGatheringPass) return; if (isShiftOperator(bin->op())) { if (isHigherShiftPriority(bin->lhs()->op()) || isHigherShiftPriority(bin->rhs()->op())) { report(bin, DiagnosticsId::DI_SHIFT_PRIORITY); } } } void CheckerVisitor::checkCompareWithContainer(const BinExpr *bin) { if (isEffectsGatheringPass) return; if (!isCompareOperator(bin->op())) return; const Expr *l = bin->lhs(); const Expr *r = bin->rhs(); if (l->op() == TO_ARRAY || r->op() == TO_ARRAY) { report(bin, DiagnosticsId::DI_CMP_WITH_CONTAINER, "array"); } auto isDeclLikeExpr = [](TreeOp op) { return op == TO_TABLE || op == TO_CLASS || op == TO_FUNCTION; }; if (isDeclLikeExpr(l->op()) || isDeclLikeExpr(r->op())) { report(bin, DiagnosticsId::DI_CMP_WITH_CONTAINER, "declaration"); } } void CheckerVisitor::checkExtendToAppend(const CallExpr *expr) { if (isEffectsGatheringPass) return; const Expr *callee = expr->callee(); const auto &args = expr->arguments(); if (callee->op() == TO_GETFIELD) { if (args.size() > 0) { Expr *arg0 = args[0]; if (arg0->op() == TO_ARRAY) { if (strcmp(callee->asGetField()->fieldName(), "extend") == 0) { report(expr, DiagnosticsId::DI_EXTEND_TO_APPEND); } } } } } void CheckerVisitor::checkMergeEmptyTable(const CallExpr *expr) { if (isEffectsGatheringPass) return; const Expr *callee = expr->callee(); const auto &args = expr->arguments(); if (callee->op() == TO_GETFIELD) { if (args.size() == 1) { Expr *arg0 = args[0]; if (arg0->op() == TO_TABLE && arg0->asTableExpr()->members().size() == 0) { if (strcmp(callee->asGetField()->fieldName(), "__merge") == 0) { report(expr, DiagnosticsId::DI_MERGE_EMPTY_TABLE); } } } } } void CheckerVisitor::checkEmptyArrayResize(const CallExpr *expr) { if (isEffectsGatheringPass) return; const Expr *callee = expr->callee(); if (callee->op() == TO_GETFIELD) { const GetFieldExpr *gf = callee->asGetField(); if (strcmp(gf->fieldName(), "resize") == 0) { const Expr *receiver = gf->receiver(); if (receiver->op() == TO_ARRAY && static_cast(receiver)->initializers().size() == 0) { report(expr, DiagnosticsId::DI_EMPTY_ARRAY_RESIZE); } } } } void CheckerVisitor::checkBoolToStrangePosition(const BinExpr *bin) { if (isEffectsGatheringPass) return; if (bin->op() != TO_IN && bin->op() != TO_INSTANCEOF) return; const Expr *lhs = maybeEval(bin->lhs()); const Expr *rhs = maybeEval(bin->rhs()); if (looksLikeBooleanExpr(lhs) && bin->op() == TO_IN) { report(bin, DiagnosticsId::DI_BOOL_PASSED_TO_STRANGE, "in"); } if (looksLikeBooleanExpr(rhs)) { report(bin, DiagnosticsId::DI_BOOL_PASSED_TO_STRANGE, bin->op() == TO_IN ? "in" : "instanceof"); } } static const char *tryExtractKeyName(const Expr *e) { if (e->op() == TO_GETFIELD) return e->asGetField()->fieldName(); if (e->op() == TO_GETSLOT) { e = e->asGetSlot()->key(); } if (e->op() == TO_LITERAL) { if (e->asLiteral()->kind() == LK_STRING) return e->asLiteral()->s(); } return nullptr; } void CheckerVisitor::checkKeyNameMismatch(const Expr *key, const Expr *e) { /* function bar() {} let tt = { "1foo" : function bar1() { .. }, // OK, not id "foo2" : function bar2() { .. }, // WARN "foo3" : function foo3() { .. }, // OK foo4 = function bar5() { ... }, // WARN foo6 = function foo6() { ... }, // OK ["foo7"] = function bar7() { ... }, // WARN ["8foo"] = function bar8() { ... } // OK, not id } tt.foo <- bar // OK tt.qux <- function fex() { .. } // WARN tt["fk"] <- function uyte() { .. } // WARN tt["f:g:h"] <- function fgh() { .. } // OK */ if (isEffectsGatheringPass) return; const char *fieldName = tryExtractKeyName(key); if (!fieldName) return; if (e->op() != TO_FUNCTION) return; const FunctionExpr *f = e->asFunctionExpr(); const char *declName = nullptr; if (!f->isLambda() && f->name()[0] != '(') { declName = f->name(); } if (!declName) return; if (!isValidId(fieldName)) return; if (strcmp(fieldName, declName) != 0) { report(e, DiagnosticsId::DI_KEY_NAME_MISMATCH, fieldName, declName); } } void CheckerVisitor::checkNewSlotNameMatch(const BinExpr *bin) { if (isEffectsGatheringPass) return; if (bin->op() != TO_NEWSLOT) return; const Expr *lhs = bin->lhs(); const Expr *rhs = bin->rhs(); checkKeyNameMismatch(lhs, rhs); } void CheckerVisitor::checkPlusString(const BinExpr *bin) { if (isEffectsGatheringPass) return; if (bin->op() != TO_ADD && bin->op() != TO_PLUSEQ) return; const Expr *l = maybeEval(bin->lhs()); const Expr *r = maybeEval(bin->rhs()); if (couldBeString(l) || couldBeString(r)) { report(bin, DiagnosticsId::DI_PLUS_STRING); } } void CheckerVisitor::checkNewGlobalSlot(const BinExpr *bin) { if (isEffectsGatheringPass) return; if (bin->op() != TO_NEWSLOT) return; const Expr *lhs = bin->lhs(); if (lhs->op() != TO_GETFIELD) return; const GetFieldExpr *gf = lhs->asGetField(); if (gf->receiver()->op() != TO_ROOT_TABLE_ACCESS) return; storeGlobalDeclaration(gf->fieldName(), bin); } void CheckerVisitor::checkUselessNullC(const BinExpr *bin) { if (isEffectsGatheringPass) return; if (bin->op() != TO_NULLC) return; const Expr *rhs = maybeEval(bin->rhs()); if (rhs->op() == TO_LITERAL && rhs->asLiteral()->kind() == LK_NULL) report(bin, DiagnosticsId::DI_USELESS_NULLC); } void CheckerVisitor::checkCannotBeNull(const BinExpr *bin) { if (isEffectsGatheringPass) return; const char *loc = nullptr; const Expr *reportee = nullptr; const Expr *checkee = nullptr; if (bin->op() == TO_NULLC) { reportee = bin->lhs(); checkee = maybeEval(reportee); loc = "null coalescing"; } else if (bin->op() == TO_NE || bin->op() == TO_EQ) { const Expr *le = maybeEval(bin->lhs()); const Expr *re = maybeEval(bin->rhs()); loc = "equal check"; if (le->op() == TO_LITERAL && le->asLiteral()->kind() == LK_NULL) { checkee = re; reportee = bin->rhs(); } else if (re->op() == TO_LITERAL && re->asLiteral()->kind() == LK_NULL) { checkee = le; reportee = bin->lhs(); } } if (checkee) reportIfCannotBeNull(checkee, reportee, loc); } void CheckerVisitor::checkCanBeSimplified(const BinExpr *bin) { if (isEffectsGatheringPass) return; if (bin->op() != TO_ANDAND && bin->op() != TO_OROR) return; const Expr *lhs = maybeEval(bin->lhs()); const Expr *rhs = maybeEval(bin->rhs()); if (isBoolRelationOperator(lhs->op()) && isBoolRelationOperator(rhs->op())) { const BinExpr *binL = static_cast(lhs); const BinExpr *binR = static_cast(rhs); if (_equalChecker.check(binL->lhs(), binR->lhs())) { int leftCmp = (binL->op() == TO_GE) || (binL->op() == TO_GT) ? 1 : -1; int rightCmp = (binR->op() == TO_GE) || (binR->op() == TO_GT) ? 1 : -1; if (leftCmp == rightCmp) { const Expr *l = maybeEval(binL->rhs()); const Expr *r = maybeEval(binR->rhs()); bool lConst = l->op() == TO_LITERAL || isUpperCaseIdentifier(l); bool rConst = r->op() == TO_LITERAL || isUpperCaseIdentifier(r); if (lConst && rConst) { report(bin, DiagnosticsId::DI_CAN_BE_SIMPLIFIED); } } } } } void CheckerVisitor::checkRangeCheck(const BinExpr *expr) { if (isEffectsGatheringPass) return; if (expr->op() != TO_OROR && expr->op() != TO_ANDAND) return; const Expr *lhs = maybeEval(expr->lhs()); const Expr *rhs = maybeEval(expr->rhs()); if (isRelationOperator(lhs->op()) && isRelationOperator(rhs->op())) { const Expr *cmpZero = nullptr, *cmpCount = nullptr; int cmpDir = 1; const BinExpr *l = static_cast(lhs); const BinExpr *r = static_cast(rhs); if (_equalChecker.check(l->lhs(), r->lhs())) { cmpZero = l->rhs(); cmpCount = r->rhs(); } else if (_equalChecker.check(l->rhs(), r->lhs())) { cmpDir = -1; cmpZero = l->lhs(); cmpCount = r->rhs(); } else if (_equalChecker.check(l->lhs(), r->rhs())) { cmpZero = l->rhs(); cmpCount = r->lhs(); } if (!cmpZero) return; assert(cmpCount); if (cmpZero->op() != TO_LITERAL || cmpZero->asLiteral()->kind() != LK_INT || cmpZero->asLiteral()->i() != 0) return; if (looksLikeElementCount(maybeEval(cmpCount))) { int leftCmp = (l->op() == TO_GE) || (l->op() == TO_GT) ? 1 : -1; int rightCmp = (r->op() == TO_GE) || (r->op() == TO_GT) ? 1 : -1; bool leftEq = (l->op() == TO_GE) || (l->op() == TO_LE); bool rightEq = (r->op() == TO_GE) || (r->op() == TO_LE); if (leftCmp * cmpDir != rightCmp && leftEq == rightEq) report(expr, DiagnosticsId::DI_RANGE_CHECK); } } } void CheckerVisitor::checkAlreadyRequired(const CallExpr *call) { if (isEffectsGatheringPass) return; if (call->arguments().size() != 1) return; const Expr *arg = maybeEval(call->arguments()[0]); if (arg->op() != TO_LITERAL) return; const LiteralExpr *l = arg->asLiteral(); if (l->kind() != LK_STRING) return; const Expr *callee = call->callee(); bool isCtor = false; const FunctionInfo *info = findFunctionInfo(callee, isCtor); if (isCtor) return; const char *name = nullptr; if (info) { name = info->declaration->name(); } else if (callee->op() == TO_ID) { name = callee->asId()->name(); } else if (callee->op() == TO_GETFIELD) { const GetFieldExpr *g = callee->asGetField(); if (g->receiver()->op() == TO_ROOT_TABLE_ACCESS) { name = g->fieldName(); } } if (!name) return; if (strcmp(name, "require") != 0 && strcmp(name, "require_optional") != 0) return; const char *moduleName = l->s(); if (!_ctx.isRequireDisabled(call->lineStart(), call->columnStart())) if (auto fv = findValueInScopes("require_optional"); fv && fv->expression && fv->expression->op() == TO_EXTERNAL_VALUE) { auto vm = _ctx.getVm(); SQInteger top = sq_gettop(vm); sq_pushobject(vm, fv->expression->asExternal()->value()); sq_pushnull(vm); sq_pushstring(vm, moduleName, -1); SQRESULT result = sq_call(vm, 2, true, false); if (SQ_SUCCEEDED(result)) { SQObject ret; if (SQ_SUCCEEDED(sq_getstackobj(vm, -1, &ret)) && !sq_isnull(ret)) { astValues[call] = addExternalValue(ret, call); } else if (do_report_missing_modules) { report(call, DI_MISSING_MODULE, moduleName); SQObjectPtr empty; astValues[call] = addExternalValue(empty, call); } } else if (do_report_missing_modules) { report(call, DI_MISSING_MODULE, moduleName); SQObjectPtr empty; astValues[call] = addExternalValue(empty, call); } sq_settop(vm, top); } if (nodeStack.size() > 2) return; // do not consider require which is not on TL if (requiredModules.find(moduleName) != requiredModules.end()) { report(call, DiagnosticsId::DI_ALREADY_REQUIRED, moduleName); } else { requiredModules.insert(moduleName); } } void CheckerVisitor::checkCallNullable(const CallExpr *call) { if (isEffectsGatheringPass) return; const Expr *c = call->callee(); if (!call->isNullable() && isPotentiallyNullable(c)) { report(c, DiagnosticsId::DI_ACCESS_POT_NULLABLE, c->op() == TO_ID ? c->asId()->name() : "expression", "function"); } } void CheckerVisitor::checkPersistCall(const CallExpr *call) { if (isEffectsGatheringPass) return; const char *calleeName = extractFunctionName(call); if (!calleeName) return; const auto &arguments = call->arguments(); const Expr *keyExpr = nullptr; if (strcmp("persist", calleeName) == 0) { if (arguments.size() < 2) return; keyExpr = arguments[0]; } else if (strcmp("mkWatched", calleeName) == 0) { if (arguments.size() < 2) return; const Expr *persistsFunc = maybeEval(arguments[0]); if (persistsFunc->op() != TO_ID || strcmp("persist", persistsFunc->asId()->name()) != 0) return; keyExpr = arguments[1]; } if (!keyExpr) return; const Expr *evalKeyExpr = maybeEval(keyExpr); if (evalKeyExpr->op() != TO_LITERAL) return; const LiteralExpr *l = evalKeyExpr->asLiteral(); if (l->kind() != LK_STRING) return; const char *key = l->s(); auto r = persistedKeys.emplace(key); if (!r.second) { report(keyExpr, DiagnosticsId::DI_DUPLICATE_PERSIST_ID, key); } } void CheckerVisitor::checkForbiddenCall(const CallExpr *call) { if (isEffectsGatheringPass) return; const char *fn = extractFunctionName(call); if (!fn) return; if (isForbiddenFunctionName(fn)) { report(call, DiagnosticsId::DI_FORBIDDEN_FUNC, fn); } } void CheckerVisitor::checkCallFromRoot(const CallExpr *call) { if (isEffectsGatheringPass) return; const char *fn = extractFunctionName(call); if (!fn) return; if (!nameLooksLikeMustBeCalledFromRoot(fn)) { return; } bool do_report = false; for (auto it = nodeStack.rbegin(); it != nodeStack.rend(); ++it) { if (it->sst == SST_TABLE_MEMBER) continue; TreeOp op = it->n->op(); if (op == TO_FUNCTION || op == TO_CLASS) { do_report = true; break; } } if (do_report) { report(call, DiagnosticsId::DI_CALL_FROM_ROOT, fn); } } void CheckerVisitor::checkForbiddenParentDir(const CallExpr *call) { if (isEffectsGatheringPass) return; const char *fn = extractFunctionName(call); if (!fn) return; if (!nameLooksLikeForbiddenParentDir(fn)) { return; } const auto &arguments = call->arguments(); if (arguments.size() < 1) return; const Expr *arg = maybeEval(arguments[0]); if (arg->op() != TO_LITERAL || arg->asLiteral()->kind() != LK_STRING) return; const char *path = arg->asLiteral()->s(); const char * p = strstr(path, ".."); if (p && (p[2] == '/' || p[2] == '\\')) { report(call, DiagnosticsId::DI_FORBIDDEN_PARENT_DIR); } } void CheckerVisitor::checkFormatArguments(const CallExpr *call) { if (isEffectsGatheringPass) return; const auto &arguments = call->arguments(); for (int32_t i = 0; i < arguments.size(); ++i) { const Expr *arg = deparenStatic(arguments[i]); if (arg->op() == TO_LITERAL && arg->asLiteral()->kind() == LK_STRING) { // -V522 int32_t formatsCount = 0; for (const char *s = arg->asLiteral()->s(); *s; ++s) { if (*s == '%') { if (*(s + 1) == '%') { s++; } else { formatsCount++; } } } if (formatsCount && formatsCount != (arguments.size() - i - 1)) { const char *name = extractFunctionName(call); if (!name) return; if (nameLooksLikeFormatFunction(name)) { report(arguments[i], DiagnosticsId::DI_FORMAT_ARGUMENTS_COUNT); } } return; } } } int32_t CheckerVisitor::normalizeParamNameLength(const char *name) { int32_t r = 0; while (*name) { if (*name != '_') ++r; ++name; } return r; } const char *CheckerVisitor::normalizeParamName(const char *name, char *buffer) { if (!buffer) { int32_t nl = normalizeParamNameLength(name); buffer = (char *)arena->allocate(nl + 1); } int32_t i = 0, j = 0; while (name[i]) { char c = name[i++]; if (c != '_') { buffer[j++] = std::tolower(c); } } buffer[j] = '\0'; return buffer; } void CheckerVisitor::checkArguments(const CallExpr *callExpr) { if (isEffectsGatheringPass) return; bool dummy; const FunctionInfo *info = findFunctionInfo(callExpr->callee(), dummy); const SQFunctionProto *proto = nullptr; const SQNativeClosure *nclosure = nullptr; const char *funcName; int numParams; int dpParameters; bool isVararg; if (info) { const FunctionExpr *decl = info->declaration; funcName = decl->name(); numParams = info->parameters.size(); isVararg = decl->isVararg(); dpParameters = 0; for (auto &p : decl->parameters()) { if (p->hasDefaultValue()) ++dpParameters; } } else { const ExternalValueExpr *ev = findExternalValue(callExpr->callee()); if (!ev) return; const SQObject& v = ev->value(); if (sq_isclosure(v)) { proto = _closure(v)->_function; funcName = sq_isstring(proto->_name) ? _stringval(proto->_name) : "unknown"; numParams = proto->_nparameters - 1; // not counting 'this' isVararg = proto->_varparams; dpParameters = proto->_ndefaultparams; } else if (sq_isfunction(v)) { assert(!"Encountered function proto - internal structure, should not happen"); return; } else if (sq_isnativeclosure(v)) { nclosure = _nativeclosure(v); if (nclosure->_nparamscheck == 0) return; funcName = sq_isstring(nclosure->_name) ? _stringval(nclosure->_name) : "unknown native"; numParams = std::abs(nclosure->_nparamscheck)-1; // not counting 'this' isVararg = nclosure->_nparamscheck < 0; dpParameters = 0; } else return; } if (numParams < 0) numParams = 0; const int effectiveParamSizeUP = std::max(isVararg ? numParams - 1 : numParams, 0); const int effectiveParamSizeLB = effectiveParamSizeUP - dpParameters; const int maxParamSize = isVararg ? INT_MAX : effectiveParamSizeUP; const auto &args = callExpr->arguments(); if (!(effectiveParamSizeLB <= args.size() && args.size() <= maxParamSize)) { report(callExpr, DiagnosticsId::DI_PARAM_COUNT_MISMATCH, funcName, effectiveParamSizeLB, maxParamSize, args.size()); report(maybeEval(callExpr->callee(), true), DI_SEE_OTHER, "the function"); } for (int i = 0; i < numParams; ++i) { const char *paramName; if (info) paramName = info->parameters[i]; else if (proto) { if (!sq_isstring(proto->_parameters[i + 1])) continue; paramName = _stringval(proto->_parameters[i + 1]); } else { if (!nclosure) assert(0); continue; } for (int j = 0; j < args.size(); ++j) { const Expr *arg = args[j]; const char *possibleArgName = nullptr; if (arg->op() == TO_ID) possibleArgName = arg->asId()->name(); else if (arg->op() == TO_GETFIELD) possibleArgName = arg->asGetField()->fieldName(); if (!possibleArgName) continue; int32_t argNL = normalizeParamNameLength(possibleArgName); char *buffer = (char *)sq_malloc(_ctx.allocContext(), argNL + 1); normalizeParamName(possibleArgName, buffer); if (i != j) { if (strcmp("vargv", paramName) != 0 || (j != numParams)) { if (strcmp(paramName, buffer) == 0) { // -V575 report(arg, DiagnosticsId::DI_PARAM_POSITION_MISMATCH, paramName); } } } sq_free(_ctx.allocContext(), buffer, argNL + 1); } } } void CheckerVisitor::checkContainerModification(const CallExpr *call) { if (isEffectsGatheringPass) return; const char *name = extractFunctionName(call); if (!name) return; if (!nameLooksLikeModifiesObject(name)) return; const Expr *callee = call->callee(); if (!callee->isAccessExpr()) return; reportModifyIfContainer(callee->asAccessExpr()->receiver(), call); } // Detect ambiguous expressions that may modify either a temporary or a persistent object static bool hasMixedLifetime(const Expr *e) { e = deparenStatic(e); const Expr *expr0 = nullptr, *expr1 = nullptr; if (e->op() == TO_NULLC) { //-V522 const BinExpr *nullc = static_cast(e); expr0 = deparenStatic(nullc->lhs()); //-V522 expr1 = deparenStatic(nullc->rhs()); //-V522 } else if (e->op() == TO_TERNARY) { //-V522 const TerExpr *ter = static_cast(e); expr0 = deparenStatic(ter->b()); //-V522 expr1 = deparenStatic(ter->c()); //-V522 } else return false; auto isDeclLikeExpr = [](TreeOp op) { return op == TO_ARRAY || op == TO_TABLE || op == TO_CLASS || op == TO_FUNCTION; }; bool isLiteral0 = isDeclLikeExpr(expr0->op()); //-V522 bool isLiteral1 = isDeclLikeExpr(expr1->op()); //-V522 return (isLiteral0 != isLiteral1); } void CheckerVisitor::checkUnwantedModification(const CallExpr *call) { if (isEffectsGatheringPass) return; const char *name = extractFunctionName(call); if (!name) return; if (!nameLooksLikeModifiesObject(name)) return; const Expr *callee = call->callee(); if (!callee->isAccessExpr()) return; if (nodeStack.back().sst == SST_NODE) { const Node *p = nodeStack.back().n; if (p->op() == TO_NEWSLOT) return; } if (hasMixedLifetime(callee->asAccessExpr()->receiver())) report(call, DiagnosticsId::DI_UNWANTED_MODIFICATION, name); } void CheckerVisitor::checkCannotBeNull(const CallExpr *call) { if (isEffectsGatheringPass) return; if (!call->isNullable()) return; const Expr *callee = call->callee(); reportIfCannotBeNull(maybeEval(callee), callee, "call"); } void CheckerVisitor::checkBooleanLambda(const CallExpr *call) { if (isEffectsGatheringPass) return; const char *fn = extractFunctionName(call); if (!fn) return; if (!nameLooksLikeFunctionTakeBooleanLambda(fn)) return; if (call->arguments().size() < 1) return; const Expr *arg = deparenStatic(call->arguments()[0]); if (arg->op() != TO_FUNCTION) // -V522 return; const FunctionExpr *f = arg->asFunctionExpr(); FunctionReturnTypeEvaluator rte(this); bool r = false; unsigned typesMask = rte.compute(f->body(), r); typesMask &= ~(RT_UNRECOGNIZED | RT_FUNCTION_CALL | RT_BOOL | RT_NUMBER | RT_NULL); if (typesMask != 0) { report(arg, DiagnosticsId::DI_BOOL_LAMBDA_REQUIRED, fn); } } void CheckerVisitor::checkCallbackReturnValue(const CallExpr *call) { if (isEffectsGatheringPass) return; const char *fn = extractFunctionName(call); if (!fn) return; if (!nameLooksLikeRequiresResultFromCallback(fn)) return; if (call->arguments().size() < 1) return; const Expr *arg = maybeEval(call->arguments()[0]); if (arg->op() != TO_FUNCTION) // -V522 return; const FunctionExpr *f = arg->asFunctionExpr(); FunctionReturnTypeEvaluator rte(this); bool allPathsReturn = false; unsigned typesMask = rte.compute(f->body(), allPathsReturn); if (typesMask & RT_NOTHING) { report(arg, DiagnosticsId::DI_CALLBACK_SHOULD_RETURN_VALUE, fn); } } void CheckerVisitor::checkCallbackShouldNotReturn(const CallExpr *call) { if (isEffectsGatheringPass) return; const char *fn = extractFunctionName(call); if (!fn) return; if (!nameLooksLikeIgnoresCallbackResult(fn)) return; if (call->arguments().size() < 1) return; const Expr *arg = maybeEval(call->arguments()[0]); if (arg->op() != TO_FUNCTION) // -V522 return; const FunctionExpr *f = arg->asFunctionExpr(); // Lambda with a single call expression like @(x) arr.append(x) is likely // a side-effect callback where the return value is incidental. if (f->isLambda()) { const Block *body = f->body()->asBlock(); const auto &stmts = body->statements(); if (stmts.size() == 1 && stmts[0]->op() == TO_RETURN) { const ReturnStatement *ret = static_cast(stmts[0]); if (ret->argument() && ret->argument()->op() == TO_CALL) return; } } FunctionReturnTypeEvaluator rte(this); bool allPathsReturn = false; unsigned typesMask = rte.compute(f->body(), allPathsReturn); unsigned valueMask = typesMask & ~(RT_NOTHING | RT_THROW | RT_FUNCTION_CALL | RT_UNRECOGNIZED); if (valueMask != 0) { report(arg, DiagnosticsId::DI_CALLBACK_SHOULD_NOT_RETURN, fn); } } void CheckerVisitor::checkAssertCall(const CallExpr *call) { // assert(x != null) or assert(x != null, "X should not be null") const Expr *callee = maybeEval(call->callee()); if (callee->op() != TO_ID) return; if (strcmp("assert", callee->asId()->name()) != 0) return; const SQUnsignedInteger argSize = call->arguments().size(); if (!argSize || argSize > 2) return; const Expr *cond = call->arguments()[0]; speculateIfConditionHeuristics(cond, currentScope, nullptr); } const char *CheckerVisitor::extractFunctionName(const CallExpr *call) { const Expr *c = maybeEval(call->callee()); const char *calleeName = nullptr; if (c->op() == TO_ID) calleeName = c->asId()->name(); else if (c->op() == TO_FUNCTION) { calleeName = c->asFunctionExpr()->name(); } else if (c->op() == TO_GETFIELD) calleeName = c->asGetField()->fieldName(); return calleeName; } void CheckerVisitor::visitBinaryBranches(Expr *lhs, Expr *rhs, bool inv) { VarScope *trunkScope = currentScope; VarScope *branchScope = nullptr; lhs->visit(this); branchScope = trunkScope->copy(arena); if (inv) { speculateIfConditionHeuristics(lhs, nullptr, branchScope); } else { speculateIfConditionHeuristics(lhs, branchScope, nullptr); } currentScope = branchScope; rhs->visit(this); trunkScope->merge(branchScope); currentScope = trunkScope; } void CheckerVisitor::visitLiteralExpr(LiteralExpr *l) { checkForgotSubst(l); } void CheckerVisitor::visitId(Id *id) { const StackSlot &ss = nodeStack.back(); const Node *parentNode = nullptr; if (ss.sst == SST_NODE) parentNode = ss.n; ValueRef *v = findValueInScopes(id->name()); if (!v) return; checkIdUsed(id, parentNode, v); } void CheckerVisitor::visitUnExpr(UnExpr *expr) { checkContainerModification(expr); Visitor::visitUnExpr(expr); } void CheckerVisitor::visitBinExpr(BinExpr *expr) { checkAndOrPriority(expr); checkBitwiseParenBool(expr); checkNullCoalescingPriority(expr); checkAssignmentToItself(expr); checkParamAssignInLambda(expr); checkSameOperands(expr); checkAlwaysTrueOrFalse(expr); checkDeclarationInArith(expr); checkIntDivByZero(expr); checkPotentiallyNullableOperands(expr); checkBitwiseToBool(expr); checkCompareWithBool(expr); checkRelativeCompareWithBool(expr); checkCopyOfExpression(expr); checkConstInBoolExpr(expr); checkShiftPriority(expr); checkCompareWithContainer(expr); checkBoolToStrangePosition(expr); checkNewSlotNameMatch(expr); checkPlusString(expr); checkNewGlobalSlot(expr); checkUselessNullC(expr); checkCannotBeNull(expr); checkCanBeSimplified(expr); checkRangeCheck(expr); Expr *lhs = expr->lhs(); Expr *rhs = expr->rhs(); switch (expr->op()) { case TO_NULLC: case TO_OROR: nodeStack.push_back({ SST_NODE, expr }); visitBinaryBranches(lhs, rhs, true); nodeStack.pop_back(); break; case TO_ANDAND: nodeStack.push_back({ SST_NODE, expr }); visitBinaryBranches(lhs, rhs, false); nodeStack.pop_back(); break; case TO_PLUSEQ: case TO_MINUSEQ: case TO_MULEQ: case TO_DIVEQ: case TO_MODEQ: case TO_NEWSLOT: case TO_ASSIGN: nodeStack.push_back({ SST_NODE, expr }); expr->rhs()->visit(this); expr->lhs()->visit(this); nodeStack.pop_back(); break; default: Visitor::visitBinExpr(expr); break; } applyBinaryToScope(expr); } void CheckerVisitor::visitTerExpr(TerExpr *expr) { checkTernaryPriority(expr); checkSameValues(expr); checkAlwaysTrueOrFalse(expr); checkCanBeSimplified(expr); nodeStack.push_back({ SST_NODE, expr }); expr->a()->visit(this); VarScope *trunkScope = currentScope; VarScope *ifTrueScope = trunkScope->copy(arena); VarScope *ifFalseScope = trunkScope->copy(arena); speculateIfConditionHeuristics(expr->a(), ifTrueScope, ifFalseScope); currentScope = ifTrueScope; expr->b()->visit(this); currentScope = ifFalseScope; expr->c()->visit(this); nodeStack.pop_back(); ifTrueScope->merge(ifFalseScope); trunkScope->copyFrom(ifTrueScope); ifTrueScope->~VarScope(); ifFalseScope->~VarScope(); currentScope = trunkScope; } void CheckerVisitor::visitIncExpr(IncExpr *expr) { const char *name = computeNameRef(deparenStatic(expr->argument()), nullptr, 0); if (name) { ValueRef *v = findValueInScopes(name); if (v) { if (currentInfo) { currentInfo->addModifiable(name, v->info->ownedScope->owner); } v->expression = nullptr; v->state = VRS_MULTIPLE; v->flagsNegative = v->flagsPositive = 0; } } Visitor::visitIncExpr(expr); } void CheckerVisitor::visitCallExpr(CallExpr *expr) { checkExtendToAppend(expr); checkMergeEmptyTable(expr); checkEmptyArrayResize(expr); checkAlreadyRequired(expr); checkCallNullable(expr); checkPersistCall(expr); checkForbiddenCall(expr); checkCallFromRoot(expr); checkForbiddenParentDir(expr); checkFormatArguments(expr); checkContainerModification(expr); checkUnwantedModification(expr); checkCannotBeNull(expr); checkBooleanLambda(expr); checkCallbackReturnValue(expr); checkCallbackShouldNotReturn(expr); applyCallToScope(expr); checkAssertCall(expr); Visitor::visitCallExpr(expr); checkArguments(expr); } void CheckerVisitor::checkBoolIndex(const GetSlotExpr *expr) { if (isEffectsGatheringPass) return; const Expr *key = maybeEval(expr->key())->asExpression(); if (isBooleanResultOperator(key->op())) { report(expr->key(), DiagnosticsId::DI_BOOL_AS_INDEX); } } void CheckerVisitor::checkNullableIndex(const GetSlotExpr *expr) { if (isEffectsGatheringPass) return; const Expr *key = expr->key(); if (!isSafeAccess(expr) && isPotentiallyNullable(key)) { report(expr->key(), DiagnosticsId::DI_POTENTIALLY_NULLABLE_INDEX); } } void CheckerVisitor::checkGlobalAccess(const GetFieldExpr *expr) { if (expr->receiver()->op() != TO_ROOT_TABLE_ACCESS) return; const BinExpr *newslot = nullptr; for (auto it = nodeStack.rbegin(); it != nodeStack.rend(); ++it) { if (it->sst != SST_NODE) continue; const Node *n = it->n; if (n->op() != TO_NEWSLOT) continue; newslot = static_cast(n); break; } if (newslot) { const Expr *lhs = deparen(newslot->lhs()); if (lhs == expr) return; } storeGlobalUsage(expr->fieldName(), expr); } void CheckerVisitor::visitGetFieldExpr(GetFieldExpr *expr) { checkAccessNullable(expr); checkEnumConstUsage(expr); checkGlobalAccess(expr); checkAccessFromStatic(expr); Visitor::visitGetFieldExpr(expr); checkExternalField(expr); } void CheckerVisitor::visitGetSlotExpr(GetSlotExpr *expr) { checkBoolIndex(expr); checkNullableIndex(expr); checkAccessNullable(expr); Visitor::visitGetSlotExpr(expr); } void CheckerVisitor::checkDuplicateSwitchCases(SwitchStatement *swtch) { if (isEffectsGatheringPass) return; const auto &cases = swtch->cases(); for (int32_t i = 0; i < int32_t(cases.size()) - 1; ++i) { for (int32_t j = i + 1; j < cases.size(); ++j) { const auto &icase = cases[i]; const auto &jcase = cases[j]; if (_equalChecker.check(icase.val, jcase.val)) { report(jcase.val, DiagnosticsId::DI_DUPLICATE_CASE); } } } } void CheckerVisitor::checkMissedBreak(const SwitchStatement *swtch) { if (isEffectsGatheringPass) return; auto &cases = swtch->cases(); FunctionReturnTypeEvaluator rtEvaluator(this); const Statement *last = nullptr; for (auto &c : cases) { if (last) { report(last, DiagnosticsId::DI_MISSED_BREAK); } last = nullptr; const Statement *stmt = c.stmt; bool r = false; rtEvaluator.compute(stmt, r); if (!r) { const Statement *uw = unwrapBodyNonEmpty(stmt); if (!uw) continue; // empty case statement -> FT const TreeOp op = uw->op(); if (op != TO_BLOCK) { if (op != TO_BREAK) last = unwrapBody(stmt); } else { const Block *b = uw->asBlock(); int32_t dummy; const Statement *tmp = lastNonEmpty(b, dummy); if (tmp && tmp->op() != TO_BREAK) { last = unwrapBody(b); } } } } if (last && swtch->defaultCase().stmt) { /* switch (x) { case A: ... break; case B: ... <-- missed break default: ... } */ report(last, DiagnosticsId::DI_MISSED_BREAK); } } void CheckerVisitor::checkDuplicateIfBranches(IfStatement *ifStmt) { if (isEffectsGatheringPass) return; if (_equalChecker.check(ifStmt->thenBranch(), ifStmt->elseBranch())) { report(ifStmt->elseBranch(), DiagnosticsId::DI_THEN_ELSE_EQUAL); } } void CheckerVisitor::checkDuplicateIfConditions(IfStatement *ifStmt) { if (isEffectsGatheringPass) return; const Statement *elseB = unwrapSingleBlock(ifStmt->elseBranch()); const Expr *duplicated = nullptr; if (elseB && elseB->op() == TO_IF) { if (findIfWithTheSameCondition(ifStmt->condition(), static_cast(elseB), duplicated)) { // TODO: high-light both conditions, original and duplicated report(duplicated, DiagnosticsId::DI_DUPLICATE_IF_EXPR); } } } static bool wrappedBody(const Statement *stmt) { if (stmt->op() != TO_BLOCK) return false; const Block *b = stmt->asBlock(); if (b->statements().size() != 1) return false; const Statement *wp = b->statements().back(); return stmt->lineStart() == wp->lineStart() && stmt->lineEnd() == wp->lineEnd() && stmt->columnStart() == wp->columnStart() && stmt->columnEnd() == wp->columnEnd(); } void CheckerVisitor::checkSuspiciousFormattingOfStetementSequence(const Statement *prev, const Statement *cur) { if (prev && cur && prev->lineStart() != cur->lineStart() && prev->columnStart() != cur->columnStart()) report(cur, DiagnosticsId::DI_INVALID_INDENTATION, std::to_string(prev->lineStart()).c_str(), std::to_string(cur->lineStart()).c_str()); } void CheckerVisitor::checkSuspiciousFormatting(const Statement *body, const Statement *stmt) { if (wrappedBody(body)) { if (stmt->lineStart() != body->lineStart() && stmt->columnStart() >= body->columnStart()) { // check if it is not `else if (...) ... ` pattern const IfStatement *elif = nullptr; const size_t nssize = nodeStack.size(); if (nssize >= 2) { for (int i = 1; i <= 2; ++i) { auto &ss = nodeStack[nssize - i]; if (ss.sst == SST_NODE && ss.n->op() == TO_IF) { elif = static_cast(ss.n); break; } } } if (elif) return; report(body, DiagnosticsId::DI_SUSPICIOUS_FMT); } } } void CheckerVisitor::checkVariableMismatchForLoop(ForStatement *loop) { if (isEffectsGatheringPass) return; const char *varname = nullptr; Node *init = loop->initializer(); Expr *cond = loop->condition(); Expr *mod = loop->modifier(); if (init) { if (init->op() == TO_ASSIGN) { Expr *l = static_cast(init)->lhs(); if (l->op() == TO_ID) varname = l->asId()->name(); } if (init->op() == TO_VAR) { VarDecl *decl = static_cast(init); varname = decl->name(); } } if (varname && cond) { if (isBinaryArith(cond)) { BinExpr *bin = static_cast(cond); Expr *l = bin->lhs(); Expr *r = bin->rhs(); if (l->op() == TO_ID) { if (strcmp(l->asId()->name(), varname) != 0) { if (r->op() != TO_ID || (strcmp(r->asId()->name(), varname) != 0)) { report(cond, DiagnosticsId::DI_MISMATCH_LOOP_VAR); } } } } } if (varname && mod) { if (isAssignExpr(mod)) { Expr *lhs = static_cast(mod)->lhs(); if (lhs->op() == TO_ID) { if (strcmp(varname, lhs->asId()->name()) != 0) { report(mod, DiagnosticsId::DI_MISMATCH_LOOP_VAR); } } } if (mod->op() == TO_INC) { Expr *arg = static_cast(mod)->argument(); if (arg->op() == TO_ID) { if (strcmp(varname, arg->asId()->name()) != 0) { report(mod, DiagnosticsId::DI_MISMATCH_LOOP_VAR); } } } } } void CheckerVisitor::checkUnterminatedLoop(LoopStatement *loop) { if (isEffectsGatheringPass) return; LoopTerminatorCollector collector(true); Statement *body = loop->body(); body->visit(&collector); if (collector.hasUnconditionalTerminator()) { const Statement *t = collector.terminator; assert(t); const char *type = terminatorOpToName(t->op()); report(t, DiagnosticsId::DI_UNCOND_TERMINATED_LOOP, type); } } void CheckerVisitor::checkEmptyWhileBody(WhileStatement *loop) { if (isEffectsGatheringPass) return; const Statement *body = unwrapBody(loop->body()); if (body && body->op() == TO_EMPTY) { report(body, DiagnosticsId::DI_EMPTY_BODY, "'while' loop"); } } void CheckerVisitor::checkEmptyThenBody(IfStatement *stmt) { if (isEffectsGatheringPass) return; const Statement *thenStmt = unwrapBody(stmt->thenBranch()); if (thenStmt && thenStmt->op() == TO_EMPTY) { report(thenStmt, DiagnosticsId::DI_EMPTY_BODY, "'then' branch of 'if'"); } } static bool terminateAssignSequence(const Expr *assignee, Node *tree) { AssignSeqTerminatorFinder finder(assignee); return finder.check(tree); } void CheckerVisitor::checkAssignedTwice(const Block *b) { if (isEffectsGatheringPass) return; const auto &statements = b->statements(); for (int32_t i = 0; i < int32_t(statements.size()) - 1; ++i) { const Expr *iexpr = unwrapExprStatement(statements[i]); if (iexpr && isAssignOp(iexpr->op())) { const BinExpr *iassgn = static_cast(iexpr); const Expr *firstAssignee = iassgn->lhs(); for (int32_t j = i + 1; j < statements.size(); ++j) { Statement *stmt = statements[j]; Expr *jexpr = unwrapExprStatement(stmt); if (jexpr && jexpr->op() == TO_ASSIGN) { const BinExpr *jassgn = static_cast(jexpr); if (_equalChecker.check(firstAssignee, jassgn->lhs())) { bool ignore = existsInTree(firstAssignee, jassgn->rhs()); if (!ignore && firstAssignee->op() == TO_GETSLOT) { const GetSlotExpr *getT = firstAssignee->asGetSlot(); ignore = indexChangedInTree(getT->key()); if (!ignore) { for (int32_t m = i + 1; m < j; ++m) { if (checkModification(getT->key(), statements[m])) { ignore = true; break; } } } } if (!ignore) { report(jassgn, DiagnosticsId::DI_ASSIGNED_TWICE); } } } else if (terminateAssignSequence(firstAssignee, stmt)) { break; } } } } } void CheckerVisitor::checkFunctionPairSimilarity(const FunctionExpr *f1, const FunctionExpr *f2) { int32_t diff = NodeDiffComputer::compute(f1->body(), f2->body(), 4); if (diff < 4) { const char *name1 = f1->name(); const char *name2 = f2->name(); if (diff == 0) report(f2, DiagnosticsId::DI_DUPLICATE_FUNC, name1, name2); else { int32_t complexity = NodeComplexityComputer::compute(f1->body(), functionComplexityThreshold * 3); if (diff <= complexity / functionComplexityThreshold) report(f2, DiagnosticsId::DI_SIMILAR_FUNC, name1, name2); } } } void CheckerVisitor::checkFunctionSimilarity(const Block *b) { if (isEffectsGatheringPass) return; const auto &statements = b->statements(); for (int32_t i = 0; i < int32_t(statements.size()); ++i) { const FunctionExpr *f1 = extractFunction(statements[i]); if (!f1) continue; int32_t complexity = NodeComplexityComputer::compute(f1->body(), functionComplexityThreshold); if (complexity >= functionComplexityThreshold) { for (int32_t j = i + 1; j < int32_t(statements.size()); ++j) { if (const FunctionExpr *f2 = extractFunction(statements[j])) { checkFunctionPairSimilarity(f1, f2); } } } } } void CheckerVisitor::checkFunctionSimilarity(const TableExpr *table) { if (isEffectsGatheringPass) return; const auto &members = table->members(); for (int32_t i = 0; i < int32_t(members.size()); ++i) { const FunctionExpr *f1 = extractFunction(members[i].value); if (!f1) continue; int32_t complexity = NodeComplexityComputer::compute(f1->body(), functionComplexityThreshold); if (complexity >= functionComplexityThreshold) { for (int32_t j = i + 1; j < int32_t(members.size()); ++j) { if (const FunctionExpr *f2 = extractFunction(members[j].value)) checkFunctionPairSimilarity(f1, f2); } } } } void CheckerVisitor::checkAssignExpressionSimilarity(const Block *b) { if (isEffectsGatheringPass) return; const auto &statements = b->statements(); for (int32_t i = 0; i < int32_t(statements.size()); ++i) { const Expr *assigned1 = extractAssignedExpression(statements[i]); if (!assigned1) continue; int32_t complexity = NodeComplexityComputer::compute(assigned1, statementComplexityThreshold); if (complexity >= statementComplexityThreshold) { for (int32_t j = i + 1; j < int32_t(statements.size()); ++j) { const Expr *assigned2 = extractAssignedExpression(statements[j]); if (!assigned2) continue; int32_t diff = NodeDiffComputer::compute(assigned1, assigned2, 3); if (diff < 3) { if (diff == 0) report(assigned2, DiagnosticsId::DI_DUPLICATE_ASSIGN_EXPR); else { complexity = NodeComplexityComputer::compute(assigned1, statementSimilarityThreshold * 2); if (diff <= complexity / statementSimilarityThreshold) report(assigned2, DiagnosticsId::DI_SIMILAR_ASSIGN_EXPR); } } } } } } // Determines if a function call's result should be utilized based on its name. // Returns true if the result SHOULD be utilized (warn if discarded). // Returns false if it's OK to discard the result (no warning). // // Special case: Boolean-named functions (is*, has*, etc.) with a single // "valuable" argument are treated as potential setters, not getters. // Example: isEnabled(true) might SET enabled state, not query it. // In this case we suppress the warning. bool CheckerVisitor::shouldCallResultBeUtilized(const char *name, const CallExpr *call) { if (!name) return false; // Functions like __merge, findvalue, filter - always should utilize result if (nameLooksLikeResultMustBeUtilized(name)) return true; // Boolean-named functions (is*, has*, get*, etc.) if (nameLooksLikeResultMustBeBoolean(name)) { const auto &args = call->arguments(); // Multiple or zero arguments - likely a getter, should utilize result if (args.size() != 1) return true; const Expr *arg = args[0]; // Single boolean expression argument (true, false, x > 0, etc.) // Looks like: isEnabled(true) - setting state to true if (looksLikeBooleanExpr(arg)) return false; // Single identifier with boolean-like name (isValid, hasItems, etc.) // Looks like: isEnabled(isCurrentlyValid) - setting state from another boolean if (arg->op() == TO_ID) { if (nameLooksLikeResultMustBeBoolean(arg->asId()->name())) return false; } // Single call to a function whose result should be utilized // Looks like: isEnabled(getValue()) - passing a meaningful value // If the inner call returns something "valuable", we're using it as input // to potentially set state, so suppress warning for the outer call if (arg->op() == TO_CALL) { const CallExpr *innerCall = arg->asCallExpr(); const Expr *callee = innerCall->callee(); const char *calleeName = nullptr; if (callee->op() == TO_ID) calleeName = callee->asId()->name(); else if (callee->op() == TO_GETFIELD) calleeName = callee->asGetField()->fieldName(); // Recursive check: if inner call's result should be utilized, // then we're passing a "valuable" result to outer function, // which suggests outer function might be a setter if (shouldCallResultBeUtilized(calleeName, innerCall)) return false; } // Try to evaluate the argument - it might resolve to a boolean // Example: let x = true; isEnabled(x) - x evaluates to true const Expr *evaled = maybeEval(arg); if (looksLikeBooleanExpr(evaled)) return false; // Single non-boolean argument - likely a getter, should utilize result return true; } return false; } void CheckerVisitor::checkUnutilizedResult(const ExprStatement *s) { if (isEffectsGatheringPass) return; const Expr *e = s->expression(); if (e->op() == TO_CALL) { const CallExpr *c = static_cast(e); const Expr *callee = c->callee(); const char *calleeName = nullptr; if (callee->op() == TO_ID) { calleeName = callee->asId()->name(); } else if (callee->op() == TO_GETFIELD) { calleeName = callee->asGetField()->fieldName(); } if (calleeName && shouldCallResultBeUtilized(calleeName, c)) { report(e, DiagnosticsId::DI_NAME_LIKE_SHOULD_RETURN, calleeName); } } else if (!isAssignExpr(e) && e->op() != TO_INC && e->op() != TO_NEWSLOT && e->op() != TO_DELETE) { report(s, DiagnosticsId::DI_UNUTILIZED_EXPRESSION); } } void CheckerVisitor::checkForgottenDo(const Block *b) { if (isEffectsGatheringPass) return; const auto &statements = b->statements(); for (int32_t i = 1; i < statements.size(); ++i) { Statement *prev = statements[i - 1]; Statement *stmt = statements[i]; if (stmt->op() == TO_WHILE && prev->op() == TO_BLOCK) { report(stmt, DiagnosticsId::DI_FORGOTTEN_DO); } } } void CheckerVisitor::checkUnreachableCode(const Block *block) { if (isEffectsGatheringPass) return; const auto &statements = block->statements(); for (int32_t i = 0; i < int32_t(statements.size()) - 1; ++i) { Statement *stmt = statements[i]; Statement *next = statements[i + 1]; if (isBlockTerminatorStatement(stmt->op())) { if (next->op() != TO_BREAK) { if (!onlyEmptyStatements(i + 1, statements)) { report(next, DiagnosticsId::DI_UNREACHABLE_CODE, terminatorOpToName(stmt->op())); break; } } } } } void CheckerVisitor::visitExprStatement(ExprStatement *stmt) { checkUnutilizedResult(stmt); Visitor::visitExprStatement(stmt); } void CheckerVisitor::visitBlock(Block *b) { checkForgottenDo(b); checkUnreachableCode(b); checkAssignedTwice(b); checkFunctionSimilarity(b); checkAssignExpressionSimilarity(b); VarScope *thisScope = currentScope; VarScope blockScope(thisScope ? thisScope->owner : nullptr, thisScope); currentScope = &blockScope; nodeStack.push_back({ SST_NODE, b }); Statement* prevStatement = nullptr; for (Statement *s : b->statements()) { s->visit(this); blockScope.evalId += 1; if (s->op() != TO_EMPTY) { checkSuspiciousFormattingOfStetementSequence(prevStatement, s); prevStatement = s; } } nodeStack.pop_back(); blockScope.parent = nullptr; blockScope.checkUnusedSymbols(this); currentScope = thisScope; } void CheckerVisitor::visitForStatement(ForStatement *loop) { checkUnterminatedLoop(loop); checkVariableMismatchForLoop(loop); checkSuspiciousFormatting(loop->body(), loop); VarScope *trunkScope = currentScope; Node *init = loop->initializer(); Expr *cond = loop->condition(); Expr *mod = loop->modifier(); VarScope *copyScope = trunkScope->copy(arena); VarScope loopScope(copyScope->owner, copyScope); currentScope = &loopScope; if (init) { init->visit(this); } if (cond) { cond->visit(this); } bool wasGatheringEffects = isEffectsGatheringPass; isEffectsGatheringPass = true; { VarScope *dummyScope = loopScope.copy(arena, false); BreakableScope bs(this, loop, dummyScope, nullptr); // null because we don't (??) interest in exit effect here currentScope = dummyScope; loop->body()->visit(this); if (mod) mod->visit(this); loopScope.merge(dummyScope); dummyScope->parent = nullptr; dummyScope->~VarScope(); } isEffectsGatheringPass = wasGatheringEffects; if (!isEffectsGatheringPass) { BreakableScope bs(this, loop, &loopScope, copyScope); currentScope = &loopScope; if (cond) speculateIfConditionHeuristics(cond, &loopScope, copyScope); loop->body()->visit(this); if (mod) mod->visit(this); } trunkScope->merge(copyScope); loopScope.checkUnusedSymbols(this); currentScope = trunkScope; } void CheckerVisitor::checkNullableContainer(const ForeachStatement *loop) { if (isEffectsGatheringPass) return; if (isPotentiallyNullable(loop->container())) { report(loop->container(), DiagnosticsId::DI_POTENTIALLY_NULLABLE_CONTAINER); } } void CheckerVisitor::visitForeachStatement(ForeachStatement *loop) { checkUnterminatedLoop(loop); checkNullableContainer(loop); checkSuspiciousFormatting(loop->body(), loop); VarScope *trunkScope = currentScope; VarScope *copyScope = trunkScope->copy(arena); VarScope loopScope(trunkScope->owner, copyScope); currentScope = &loopScope; const VarDecl *idx = loop->idx(); const VarDecl *val = loop->val(); if (idx) { SymbolInfo *info = makeSymbolInfo(SK_FOREACH); ValueRef *v = makeValueRef(info); v->state = VRS_UNKNOWN; v->expression = nullptr; info->declarator.v = idx; info->ownedScope = &loopScope; //-V506 declareSymbol(idx->name(), v); } if (val) { SymbolInfo *info = makeSymbolInfo(SK_FOREACH); ValueRef *v = makeValueRef(info); v->state = VRS_UNKNOWN; v->expression = nullptr; info->declarator.v = val; info->ownedScope = &loopScope; //-V506 declareSymbol(val->name(), v); } StackSlot slot; slot.n = loop; slot.sst = SST_NODE; nodeStack.push_back(slot); loop->container()->visit(this); bool wasGatheringEffects = isEffectsGatheringPass; isEffectsGatheringPass = true; { VarScope *dummyScope = loopScope.copy(arena, false); BreakableScope bs(this, loop, dummyScope, nullptr); // null because we don't (??) interest in exit effect here currentScope = dummyScope; loop->body()->visit(this); loopScope.merge(dummyScope); dummyScope->parent = nullptr; dummyScope->~VarScope(); } isEffectsGatheringPass = wasGatheringEffects; if (!isEffectsGatheringPass) { BreakableScope bs(this, loop, &loopScope, copyScope); currentScope = &loopScope; loop->body()->visit(this); } nodeStack.pop_back(); trunkScope->merge(copyScope); loopScope.checkUnusedSymbols(this); currentScope = trunkScope; } void CheckerVisitor::visitWhileStatement(WhileStatement *loop) { checkUnterminatedLoop(loop); checkEmptyWhileBody(loop); checkSuspiciousFormatting(loop->body(), loop); loop->condition()->visit(this); VarScope *trunkScope = currentScope; VarScope *loopScope = trunkScope->copy(arena); currentScope = loopScope; bool wasGatheringEffects = isEffectsGatheringPass; isEffectsGatheringPass = true; { BreakableScope bs(this, loop, loopScope, nullptr); // null because we don't (??) interest in exit effect here loop->body()->visit(this); } isEffectsGatheringPass = wasGatheringEffects; if (!isEffectsGatheringPass) { BreakableScope bs(this, loop, loopScope, trunkScope); speculateIfConditionHeuristics(loop->condition(), loopScope, trunkScope); loop->body()->visit(this); } trunkScope->merge(loopScope); loopScope->~VarScope(); currentScope = trunkScope; } void CheckerVisitor::visitDoWhileStatement(DoWhileStatement *loop) { checkUnterminatedLoop(loop); VarScope *trunkScope = currentScope; VarScope *loopScope = trunkScope->copy(arena); currentScope = loopScope; bool wasGatheringEffects = isEffectsGatheringPass; isEffectsGatheringPass = true; { BreakableScope bs(this, loop, loopScope, nullptr); // null because we don't (??) interest in exit effect here loop->body()->visit(this); loop->condition()->visit(this); } isEffectsGatheringPass = wasGatheringEffects; if (!isEffectsGatheringPass) { BreakableScope bs(this, loop, loopScope, trunkScope); loop->body()->visit(this); loop->condition()->visit(this); } trunkScope->copyFrom(loopScope); loopScope->~VarScope(); currentScope = trunkScope; } void CheckerVisitor::visitBreakStatement(BreakStatement *breakStmt) { VarScope *trunkScope = currentScope; BreakableScope *bs = breakScope; assert(bs); if (bs->kind != BSK_LOOP) return; if (bs->exitScope) bs->exitScope->mergeUnbalanced(trunkScope); } void CheckerVisitor::visitContinueStatement(ContinueStatement *continueStmt) { VarScope *trunkScope = currentScope; BreakableScope *bs = breakScope; assert(bs); while (bs->kind != BSK_LOOP) bs = bs->parent; assert(bs->loopScope); bs->loopScope->mergeUnbalanced(trunkScope); } bool CheckerVisitor::detectTypeOfPattern(const Expr *expr, const Expr *&res_checkee, const LiteralExpr *&res_lit) { // Either of 'typeof checkee ==/!= "lit"' or 'type(checkee) ==/!= "lit" expr = deparenStatic(expr); TreeOp op = expr->op(); // -V522 if (op != TO_EQ && op != TO_NE) return false; // not typeof pattern const BinExpr *bin = expr->asBinExpr(); const Expr *lhs = deparenStatic(bin->lhs()); const Expr *rhs = deparenStatic(bin->rhs()); const LiteralExpr *l_lit = lhs->op() == TO_LITERAL ? lhs->asLiteral() : nullptr; // -V522 const LiteralExpr *r_lit = rhs->op() == TO_LITERAL ? rhs->asLiteral() : nullptr; // -V522 if (!l_lit && !r_lit) return false; if (l_lit && r_lit) return false; const LiteralExpr *lit = l_lit ? l_lit : r_lit; if (lit->kind() != LK_STRING) // -V522 return false; const Expr *checkExpr = lit == lhs ? rhs : lhs; if (checkExpr->op() == TO_TYPEOF) { // -V522 const UnExpr *typeOf = static_cast(checkExpr); res_checkee = deparenStatic(typeOf->argument()); // -V522 res_lit = lit; return true; } if (checkExpr->op() == TO_CALL) { // check for `type(check)` pattern const CallExpr *call = checkExpr->asCallExpr(); const Expr *callee = maybeEval(call->callee()); if (callee->op() != TO_ID || strcmp("type", callee->asId()->name()) != 0) { return false; } if (call->arguments().size() != 1) return false; res_checkee = deparenStatic(call->arguments()[0]); res_lit = lit; return true; } return false; } bool CheckerVisitor::detectNullCPattern(TreeOp op, const Expr *cond, const Expr *&checkee) { // detect patterns like `(o?.x ?? D) D` which implies o not null // (o?.f ?? D) != V -- then-branch implies `o` non-null // (o?.f ?? D) == V -- assume else-branch implies `o` non-null // (o?.f ?? D) > V -- then-b implies `o` non-null // (o?.f ?? D) < V -- then-b implies `o` non-null // (o?.f ?? D) >= V -- else-branch implies `o` non-null // (o?.f ?? D) <= V -- else-branch implies `o` non-null if (op != TO_LT && op != TO_GT && op != TO_EQ && op != TO_NE && op != TO_LE && op != TO_GE) { return false; } const BinExpr *bin = cond->asBinExpr(); const Expr *lhs = deparenStatic(bin->lhs()); const Expr *rhs = deparenStatic(bin->rhs()); if (lhs->op() != TO_NULLC && rhs->op() != TO_NULLC) // -V522 return false; const BinExpr *nullc = lhs->op() == TO_NULLC ? lhs->asBinExpr() : rhs->asBinExpr(); const Expr *V = lhs == nullc ? rhs : lhs; const Expr *D = deparenStatic(nullc->rhs()); const Expr *candidate = deparenStatic(nullc->lhs()); const Expr *reciever = extractReceiver(candidate); if (reciever == nullptr) return false; if (V->op() == TO_ID || _equalChecker.check(D, V)) { // -V522 checkee = reciever; return true; } return false; } void CheckerVisitor::speculateIfConditionHeuristics(const Expr *cond, VarScope *thenScope, VarScope *elseScope, bool inv) { std::unordered_set visited; speculateIfConditionHeuristics(cond, thenScope, elseScope, visited, -1, 0, inv); } #define NULL_CHECK_F 1 void CheckerVisitor::speculateIfConditionHeuristics(const Expr *cond, VarScope *thenScope, VarScope *elseScope, std::unordered_set &visited, int32_t evalId, unsigned flags, bool inv) { cond = deparenStatic(cond); if (visited.find(cond) != visited.end()) { return; } visited.emplace(cond); TreeOp op = cond->op(); VarScope *thisScope = currentScope; if (op == TO_NOT) { const UnExpr *u = static_cast(cond); return speculateIfConditionHeuristics(u->argument(), thenScope, elseScope, visited, evalId, flags, !inv); } bool invertLR = false; if (op == TO_NE) { op = TO_EQ; inv = !inv; } else if (op == TO_OROR) { // apply a || b <==> !(!a && !b) op = TO_ANDAND; invertLR = true; inv = !inv; } if (inv) { std::swap(thenScope, elseScope); } const Expr *nullcCheckee = nullptr; if (detectNullCPattern(op, cond, nullcCheckee)) { // (o?.f ?? D) == V -- assume else-branch implies `o` non-null // (o?.f ?? D) > V -- then-b implies `o` non-null // (o?.f ?? D) < V -- then-b implies `o` non-null if (op == TO_LE || op == TO_GE) { if (elseScope) { currentScope = elseScope; setValueFlags(nullcCheckee, 0, RT_NULL); } } else if (op == TO_EQ) { if (elseScope) { currentScope = elseScope; setValueFlags(nullcCheckee, 0, RT_NULL); } } else { assert(op == TO_LT || op == TO_GT); if (thenScope) { currentScope = thenScope; setValueFlags(nullcCheckee, 0, RT_NULL); } } currentScope = thisScope; // -V519 return; } if (op == TO_NULLC) { // o?.f ?? false // o?.f ?? true const BinExpr *bin = cond->asBinExpr(); const Expr *lhs = deparenStatic(bin->lhs()); const Expr *rhs = deparenStatic(bin->rhs()); if (rhs->op() != TO_LITERAL) { // -V522 return; } const LiteralExpr *lit = rhs->asLiteral(); bool nullCond; switch (lit->kind()) { case LK_BOOL: nullCond = rhs->asLiteral()->b(); break; case LK_NULL: nullCond = false; break; case LK_INT: nullCond = lit->i() != 0; break; case LK_FLOAT: nullCond = lit->f() != 0.0f; break; case LK_STRING: nullCond = true; break; default: assert(0 && "unknown literal kind"); break; } unsigned pf, nf; if (nullCond) { pf = RT_NULL; nf = 0; } else { pf = 0; nf = RT_NULL; } const Expr *receiver = extractReceiver(lhs); if (!receiver) return; if (thenScope && !nullCond) { currentScope = thenScope; setValueFlags(receiver, pf, nf); } if (elseScope && nullCond) { currentScope = elseScope; setValueFlags(receiver, nf, pf); // -V764 } currentScope = thisScope; // -V519 return; } if (evalId == -1 && op == TO_CALL && thenScope) { // if condition looks like `if (foo(x)) x.bar()` consider call as null check currentScope = thenScope; const CallExpr *call = cond->asCallExpr(); for (auto parg : call->arguments()) { auto arg = deparenStatic(parg); if (arg->op() == TO_ID) { // -V522 setValueFlags(arg, 0, RT_NULL); } } const Expr *callee = extractReceiver(call->callee()); if (callee) { setValueFlags(callee, 0, RT_NULL); } currentScope = thisScope; // -V519 return; } if (op == TO_ID) { int32_t evalIndex = -1; const Expr *eval = maybeEval(cond, evalIndex); bool notOverriden = evalId == -1 || evalIndex < evalId; if (thenScope && notOverriden) { // set iff it was explicit check like `if (o) { .. }` // otherwise there could be complexities, see intersected_assignment.nut currentScope = thenScope; setValueFlags(cond, 0, RT_NULL); currentScope = thisScope; } if (elseScope && notOverriden && (flags & NULL_CHECK_F)) { // set NULL iff it was explicit null check `if (o == null) { ... }` otherwise it could not be null, see w233_inc_in_for.nut currentScope = elseScope; setValueFlags(cond, RT_NULL, 0); currentScope = thisScope; } if (eval != cond) { // let cond = x != null // if (cond) { ... } speculateIfConditionHeuristics(eval, thenScope, elseScope, visited, evalIndex, flags, false); } return; } if (op == TO_GETSLOT || op == TO_GETFIELD) { // x?[y] const AccessExpr *acc = static_cast(cond); const Expr *reciever = extractReceiver(deparenStatic(acc->receiver())); if (reciever && thenScope) { currentScope = thenScope; setValueFlags(reciever, 0, RT_NULL); } if (op == TO_GETSLOT) { const Expr *key = deparenStatic(acc->asGetSlot()->key()); if (thenScope) { currentScope = thenScope; setValueFlags(key, 0, RT_NULL); } } currentScope = thisScope; // -V519 return; } if (op == TO_EQ) { const BinExpr *bin = cond->asBinExpr(); const Expr *lhs = deparenStatic(bin->lhs()); const Expr *rhs = deparenStatic(bin->rhs()); const LiteralExpr *lhs_lit = lhs->op() == TO_LITERAL ? lhs->asLiteral() : nullptr; // -V522 const LiteralExpr *rhs_lit = rhs->op() == TO_LITERAL ? rhs->asLiteral() : nullptr; // -V522 const LiteralExpr *lit = lhs_lit ? lhs_lit : rhs_lit; const Expr *testee = lit == lhs ? rhs : lhs; if (!lit) return; if (lit->kind() == LK_NULL) { speculateIfConditionHeuristics(testee, elseScope, thenScope, visited, evalId, flags | NULL_CHECK_F, false); return; } const Expr *receiver = extractReceiver(testee); if (receiver && thenScope) { currentScope = thenScope; setValueFlags(receiver, 0, RT_NULL); currentScope = thisScope; } } if (isRelationOperator(op)) { const BinExpr *bin = cond->asBinExpr(); const Expr *lhs = deparenStatic(bin->lhs()); const Expr *rhs = deparenStatic(bin->rhs()); const Id *lhs_id = lhs->op() == TO_ID ? lhs->asId() : nullptr; // -V522 const Id *rhs_id = rhs->op() == TO_ID ? rhs->asId() : nullptr; // -V522 if (thenScope) { currentScope = thenScope; if (lhs_id) { setValueFlags(lhs_id, 0, RT_NULL); } if (rhs_id) { setValueFlags(rhs_id, 0, RT_NULL); } currentScope = thisScope; } return; } const LiteralExpr *typeLit = nullptr; const Expr *typeCheckee = nullptr; if (detectTypeOfPattern(cond, typeCheckee, typeLit)) { assert(typeCheckee && typeLit); const Id *checkeeId = extractReceiver(typeCheckee); if (!checkeeId) return; bool nullCheck = strcmp(typeLit->s(), "null") == 0; // todo: should it be more precise in case of avaliable names bool accessCheck = typeCheckee != checkeeId; // if it's null check we could know for sure that true-branch implies NULL and false-branch implies NON_NULL // if it's not a null check we could only guarantee that true-branch implies NON_NULL unsigned pt = 0, nt = 0, pe = 0, ne = 0; if (nullCheck) { if (accessCheck) { ne = RT_NULL; } else { pt = RT_NULL; ne = RT_NULL; } } else { nt = RT_NULL; } if (thenScope) { currentScope = thenScope; setValueFlags(checkeeId, pt, nt); } if (elseScope) { currentScope = elseScope; setValueFlags(checkeeId, pe, ne); } currentScope = thisScope; // -V519 return; } if (op == TO_ANDAND) { const BinExpr *bin = cond->asBinExpr(); const Expr *lhs = bin->lhs(); const Expr *rhs = bin->rhs(); VarScope *lhsEScope = nullptr; VarScope *rhsEScope = nullptr; // In '&&' operator all effects of the left-hand side work for the right side as well if (elseScope) { lhsEScope = elseScope->copy(arena); rhsEScope = elseScope->copy(arena); } speculateIfConditionHeuristics(lhs, thenScope, lhsEScope, visited, evalId, flags, invertLR); speculateIfConditionHeuristics(rhs, thenScope, rhsEScope, visited, evalId, flags, invertLR); if (elseScope) { // In contrast in `false`-branch there is no such integral effect, all we could say is something common of both side so // to compute that `common` part intersect lhs with rhs lhsEScope->intersectScopes(rhsEScope); // -V522 elseScope->copyFrom(lhsEScope); lhsEScope->~VarScope(); // -V522 rhsEScope->~VarScope(); // -V522 } return; } if (op == TO_INSTANCEOF || op == TO_IN) { const BinExpr *bin = cond->asBinExpr(); const Expr *lhs = bin->lhs(); const Expr *rhs = bin->rhs(); const Expr *checkee = extractReceiver(lhs); if (checkee && thenScope) { currentScope = thenScope; setValueFlags(checkee, 0, RT_NULL); } if (op == TO_IN) { checkee = extractReceiver(rhs); if (checkee && thenScope) { currentScope = thenScope; setValueFlags(checkee, 0, RT_NULL); } } currentScope = thisScope; // -V519 return; } } void CheckerVisitor::visitIfStatement(IfStatement *ifstmt) { checkEmptyThenBody(ifstmt); checkDuplicateIfConditions(ifstmt); checkDuplicateIfBranches(ifstmt); checkAlwaysTrueOrFalse(ifstmt->condition()); checkSuspiciousFormatting(ifstmt->thenBranch(), ifstmt); VarScope *trunkScope = currentScope; VarScope *thenScope = trunkScope->copy(arena); VarScope *dummyScope = trunkScope->copy(arena); VarScope *elseScope = ifstmt->elseBranch() ? trunkScope->copy(arena) : nullptr; nodeStack.push_back({ SST_NODE, ifstmt }); currentScope = dummyScope; ifstmt->condition()->visit(this); currentScope = trunkScope; // pass fall-through branch to not lose effects speculateIfConditionHeuristics(ifstmt->condition(), thenScope, elseScope ? elseScope : trunkScope); currentScope = thenScope; ifstmt->thenBranch()->visit(this); bool isThenFT = isFallThroughBranch(ifstmt->thenBranch()); bool isElseFT = true; if (ifstmt->elseBranch()) { currentScope = elseScope; ifstmt->elseBranch()->visit(this); isElseFT = isFallThroughBranch(ifstmt->elseBranch()); } nodeStack.pop_back(); if (isThenFT && isElseFT) { if (elseScope) { thenScope->merge(elseScope); trunkScope->copyFrom(thenScope); } else { trunkScope->merge(thenScope); } } else if (isThenFT) { trunkScope->copyFrom(thenScope); } else if (elseScope && isElseFT) { trunkScope->copyFrom(elseScope); } if (elseScope) elseScope->~VarScope(); thenScope->~VarScope(); currentScope = trunkScope; } void CheckerVisitor::visitSwitchStatement(SwitchStatement *swtch) { checkDuplicateSwitchCases(swtch); checkMissedBreak(swtch); Expr *expr = swtch->expression(); expr->visit(this); auto &cases = swtch->cases(); VarScope *trunkScope = currentScope; BreakableScope breakScope(this, swtch); // -V688 std::vector scopes; for (auto &c : cases) { c.val->visit(this); VarScope *caseScope = trunkScope->copy(arena); currentScope = caseScope; c.stmt->visit(this); scopes.push_back(caseScope); } if (swtch->defaultCase().stmt) { VarScope *defaultScope = trunkScope->copy(arena); currentScope = defaultScope; swtch->defaultCase().stmt->visit(this); scopes.push_back(defaultScope); } for (VarScope *s : scopes) { trunkScope->merge(s); s->~VarScope(); } currentScope = trunkScope; } void CheckerVisitor::visitTryStatement(TryStatement *tryStmt) { Statement *t = tryStmt->tryStatement(); Id *id = tryStmt->exceptionId(); Statement *c = tryStmt->catchStatement(); VarScope *trunkScope = currentScope; VarScope *tryScope = trunkScope->copy(arena); currentScope = tryScope; t->visit(this); VarScope *copyScope = trunkScope->copy(arena); VarScope catchScope(copyScope->owner, copyScope); currentScope = &catchScope; SymbolInfo *info = makeSymbolInfo(SK_EXCEPTION); ValueRef *v = makeValueRef(info); v->state = VRS_UNKNOWN; v->expression = nullptr; info->declarator.x = id; info->ownedScope = &catchScope; //-V506 declareSymbol(id->name(), v); id->visit(this); c->visit(this); tryScope->merge(copyScope); trunkScope->copyFrom(tryScope); tryScope->~VarScope(); currentScope = trunkScope; } const char *CheckerVisitor::findSlotNameInStack(const Node *decl) { auto it = nodeStack.rbegin(); auto ie = nodeStack.rend(); while (it != ie) { auto slot = *it; if (slot.sst == SST_NODE) { const Node *n = slot.n; if (n->op() == TO_NEWSLOT) { const BinExpr *bin = static_cast(n); Expr *lhs = bin->lhs(); Expr *rhs = bin->rhs(); if (rhs == decl) { if (lhs->op() == TO_LITERAL) { if (lhs->asLiteral()->kind() == LK_STRING) { return lhs->asLiteral()->s(); } } return nullptr; } } } else { assert(slot.sst == SST_TABLE_MEMBER); Expr *lhs = slot.member->key; Expr *rhs = slot.member->value; if (rhs == decl) { if (lhs->op() == TO_LITERAL) { if (lhs->asLiteral()->kind() == LK_STRING) { return lhs->asLiteral()->s(); } } return nullptr; } } ++it; } return nullptr; } void CheckerVisitor::checkFunctionReturns(FunctionExpr *func) { if (isEffectsGatheringPass) return; const char *name = func->name(); if (!name || name[0] == '(') { name = findSlotNameInStack(func); } bool dummy; unsigned returnFlags = FunctionReturnTypeEvaluator(this).compute(func->body(), dummy); bool reported = false; if (returnFlags & ~(RT_BOOL | RT_UNRECOGNIZED | RT_FUNCTION_CALL | RT_NULL)) { // Null is castable to boolean if (nameLooksLikeResultMustBeBoolean(name)) { Node *reportNode = func->nameId(); if (!reportNode) // just for safety reportNode = func; report(reportNode, DiagnosticsId::DI_NAME_RET_BOOL, name); reported = true; } } if (!!(returnFlags & RT_NOTHING) && !!(returnFlags & (RT_NUMBER | RT_STRING | RT_TABLE | RT_CLASS | RT_ARRAY | RT_CLOSURE | RT_UNRECOGNIZED | RT_THROW))) { if ((returnFlags & RT_THROW) == 0) report(func, DiagnosticsId::DI_NOT_ALL_PATH_RETURN_VALUE); reported = true; } else if (returnFlags) { unsigned flagsDiff = returnFlags & ~(RT_THROW | RT_NOTHING | RT_NULL | RT_UNRECOGNIZED | RT_FUNCTION_CALL); if (flagsDiff) { bool powerOfTwo = !(flagsDiff & (flagsDiff - 1)); if (!powerOfTwo) { report(func, DiagnosticsId::DI_RETURNS_DIFFERENT_TYPES); reported = true; } } } if (!reported) { if (!!(returnFlags & RT_NOTHING) && nameLooksLikeFunctionMustReturnResult(name)) { report(func, DiagnosticsId::DI_NAME_EXPECTS_RETURN, name); } } } void CheckerVisitor::checkAccessNullable(const DestructuringDecl *dd) { if (isEffectsGatheringPass) return; const Expr *i = dd->initExpression(); const Expr *initializer = i; if (isPotentiallyNullable(initializer)) { bool allHasDefault = true; for (auto d : dd->declarations()) { if (d->initializer() == nullptr) { allHasDefault = false; break; } } if (!allHasDefault) { report(dd, DiagnosticsId::DI_ACCESS_POT_NULLABLE, i->op() == TO_ID ? i->asId()->name() : "expression", "container"); } } } void CheckerVisitor::checkAccessNullable(const AccessExpr *acc) { if (isEffectsGatheringPass) return; const Expr *r = acc->receiver(); if (!isSafeAccess(acc) && isPotentiallyNullable(r)) { report(r, DiagnosticsId::DI_ACCESS_POT_NULLABLE, "expression", "container"); } } void CheckerVisitor::checkEnumConstUsage(const GetFieldExpr *acc) { if (isEffectsGatheringPass) return; char buffer[64] = { 0 }; const char *receiverName = computeNameRef(acc->receiver(), buffer, sizeof buffer); if (!receiverName) return; const ValueRef *enumV = findValueInScopes(receiverName); if (!enumV || enumV->info->kind != SK_ENUM) return; const char *fqn = enumFqn(arena, receiverName, acc->fieldName()); const ValueRef *constV = findValueInScopes(fqn); if (!constV) { // very suspicious return; } constV->info->used = true; } void CheckerVisitor::visitTableExpr(TableExpr *table) { checkFunctionSimilarity(table); for (auto &member : table->members()) { StackSlot slot; slot.sst = SST_TABLE_MEMBER; slot.member = &member; checkKeyNameMismatch(member.key, member.value); nodeStack.push_back(slot); member.key->visit(this); member.value->visit(this); nodeStack.pop_back(); } } void CheckerVisitor::visitClassExpr(ClassExpr *klass) { nodeStack.push_back({ SST_NODE, klass }); if (klass->classKey()) klass->classKey()->visit(this); if (klass->classBase()) klass->classBase()->visit(this); visitTableExpr(klass); nodeStack.pop_back(); } void CheckerVisitor::visitFunctionExpr(FunctionExpr *func) { VarScope *parentScope = currentScope; VarScope *copyScope = parentScope->copy(arena, true); VarScope functionScope(func, copyScope); SymbolInfo *info = makeSymbolInfo(SK_FUNCTION); ValueRef *v = makeValueRef(info); v->state = VRS_DECLARED; v->expression = nullptr; info->declarator.f = func; info->ownedScope = currentScope; info->used = true; declareSymbol(func->name(), v); pushFunctionScope(&functionScope, func); FunctionInfo *oldInfo = currentInfo; FunctionInfo *newInfo = functionInfoMap[func]; currentInfo = newInfo; assert(newInfo); checkFunctionReturns(func); Visitor::visitFunctionExpr(func); if (oldInfo) { oldInfo->joinModifiable(newInfo); } functionScope.checkUnusedSymbols(this); currentInfo = oldInfo; currentScope = parentScope; } ValueRef *CheckerVisitor::findValueInScopes(const char *ref) { if (!ref) return nullptr; VarScope *current = currentScope; VarScope *s = current; while (s) { auto &symbols = s->symbols; auto it = symbols.find(ref); if (it != symbols.end()) { return it->second; } s = s->parent; } return nullptr; } void CheckerVisitor::applyAssignmentToScope(const BinExpr *bin) { assert(bin->op() == TO_ASSIGN); const Expr *lhs = bin->lhs(); const Expr *rhs = bin->rhs(); if (lhs->op() != TO_ID) return; const char *name = lhs->asId()->name(); ValueRef *v = findValueInScopes(name); if (!v) { // TODO: what if declarator == null SymbolInfo *info = makeSymbolInfo(SK_VAR); v = makeValueRef(info); currentScope->symbols[name] = v; info->ownedScope = currentScope; info->declarator.v = nullptr; if (currentInfo) { currentInfo->addModifiable(name, info->ownedScope->owner); } } v->expression = rhs; v->state = VRS_EXPRESSION; v->flagsNegative = v->flagsPositive = 0; v->assigned = true; v->lastAssigneeScope = currentScope; v->evalIndex = currentScope->evalId; if (isPotentiallyNullable(rhs)) { v->flagsPositive |= RT_NULL; } } void CheckerVisitor::applyAssignEqToScope(const BinExpr *bin) { assert(TO_PLUSEQ <= bin->op() && bin->op() <= TO_MODEQ); const Expr *lhs = bin->lhs(); const char *name = computeNameRef(lhs, nullptr, 0); if (!name) return; ValueRef *v = findValueInScopes(name); if (v) { if (currentInfo) { currentInfo->addModifiable(name, v->info->ownedScope->owner); } v->kill(); v->evalIndex = currentScope->evalId; } } void CheckerVisitor::applyBinaryToScope(const BinExpr *bin) { switch (bin->op()) { case TO_ASSIGN: return applyAssignmentToScope(bin); case TO_PLUSEQ: case TO_MINUSEQ: case TO_MULEQ: case TO_DIVEQ: case TO_MODEQ: return applyAssignEqToScope(bin); default: break; } } static const char double_colon_str[] = "::"; static const char base_str[] = "base"; int32_t CheckerVisitor::computeNameLength(const Expr *e) { switch (e->op()) // -V522 { case TO_GETFIELD: return computeNameLength(e->asGetField()); //case TO_GETSLOT: return computeNameLength(e->asGetSlot()); case TO_ID: return strlen(e->asId()->name()); case TO_ROOT_TABLE_ACCESS: return strlen(double_colon_str); case TO_BASE: return strlen(base_str); default: return -1; } } void CheckerVisitor::computeNameRef(const Expr *e, char *b, int32_t &ptr, int32_t size) { switch (e->op()) { case TO_GETFIELD: return computeNameRef(e->asGetField(), b, ptr, size); //case TO_GETSLOT: return computeNameRef(lhs->asGetSlot()); case TO_ID: { int32_t l = snprintf(&b[ptr], size - ptr, "%s", e->asId()->name()); ptr += l; break; } case TO_ROOT_TABLE_ACCESS: snprintf(&b[ptr], size - ptr, "%s", double_colon_str); ptr += strlen(double_colon_str); break; case TO_BASE: snprintf(&b[ptr], size - ptr, "%s", base_str); ptr += strlen(base_str); break; default: assert(0); } } const char *CheckerVisitor::computeNameRef(const Expr *lhs, char *buffer, size_t bufferSize) { int32_t length = computeNameLength(lhs); if (length < 0) return nullptr; char *result = buffer; if (!result || bufferSize < (length + 1)) { result = (char *)arena->allocate(length + 1); } int32_t ptr = 0; computeNameRef(lhs, result, ptr, length + 1); assert(ptr == length); return result; } void CheckerVisitor::setValueFlags(const Expr *lvalue, unsigned pf, unsigned nf) { char buffer[128] = { 0 }; const char *name = computeNameRef(lvalue, buffer, sizeof buffer); if (!name) return; ValueRef *v = findValueInScopes(name); if (v) { v->flagsPositive |= pf; v->flagsPositive &= ~nf; v->flagsNegative |= nf; v->flagsNegative &= ~pf; } } const ValueRef *CheckerVisitor::findValueForExpr(const Expr *e) { e = deparenStatic(e); if (!e) return nullptr; if (auto it = astValues.find(e); it != astValues.end()) return it->second; char buffer[128] = { 0 }; const char *n = computeNameRef(e, buffer, sizeof buffer); if (!n) { return nullptr; } return findValueInScopes(n); } bool CheckerVisitor::isSafeAccess(const AccessExpr *acc) { if (acc->isNullable()) return true; const Expr *recevier = acc->receiver(); if (recevier->isAccessExpr()) { return isSafeAccess(recevier->asAccessExpr()); } return false; } bool CheckerVisitor::isPotentiallyNullable(const Expr *e) { std::unordered_set v; return isPotentiallyNullable(e, v); } bool CheckerVisitor::isPotentiallyNullable(const Expr *e, std::unordered_set &visited) { e = deparenStatic(e); auto existed = visited.find(e); if (existed != visited.end()) { return false; } visited.emplace(e); const ValueRef *v = findValueForExpr(e); if (v) { if (v->flagsPositive & RT_NULL) return true; if (v->flagsNegative & RT_NULL) return false; // Check aliased source variable's current flags if it wasn't reassigned. // This handles: let y = x; if (!x) return; y.field // (y is non-null via x) if (v->origin && v->origin->evalIndex == v->originEvalIndex) { if (v->origin->flagsNegative & RT_NULL) return false; } } e = maybeEval(e); if (e->op() == TO_LITERAL) { const LiteralExpr *l = e->asLiteral(); return l->kind() == LK_NULL; } if (e->isAccessExpr()) { if (e->asAccessExpr()->isNullable()) { return true; } } if (e->op() == TO_CALL) { const CallExpr *call = static_cast(e); if (call->isNullable()) { return true; } const char *funcName = nullptr; const Expr *callee = call->callee(); if (callee->op() == TO_ID) { funcName = callee->asId()->name(); } else if (callee->op() == TO_GETFIELD) { funcName = callee->asGetField()->fieldName(); } if (funcName) { if (canFunctionReturnNull(funcName)) { return true; } } } if (e->op() == TO_NULLC) { return isPotentiallyNullable(static_cast(e)->rhs(), visited); } if (e->op() == TO_TERNARY) { const TerExpr *t = static_cast(e); return isPotentiallyNullable(t->b(), visited) || isPotentiallyNullable(t->c(), visited); } v = findValueForExpr(e); if (v) { switch (v->state) { case VRS_EXPRESSION: case VRS_INITIALIZED: return isPotentiallyNullable(v->expression, visited); default: return false; } } return false; } bool CheckerVisitor::couldBeString(const Expr *e) { if (!e) return false; e = deparenStatic(e); if (e->op() == TO_LITERAL) { // -V522 return e->asLiteral()->kind() == LK_STRING; } if (e->op() == TO_NULLC) { const BinExpr *b = static_cast(e); if (b->rhs()->op() == TO_LITERAL && b->rhs()->asLiteral()->kind() == LK_STRING) // -V522 return couldBeString(b->lhs()); } if (e->op() == TO_CALL) { // -V522 const char *name = nullptr; const Expr *callee = static_cast(e)->callee(); if (callee->op() == TO_ID) name = callee->asId()->name(); else if (callee->op() == TO_GETFIELD) name = callee->asGetField()->fieldName(); if (name) { return nameLooksLikeResultMustBeString(name) || strcmp(name, "loc") == 0; } } if (e->op() == TO_ADD) { // -V522 // check for string concat const BinExpr *b = e->asBinExpr(); return couldBeString(b->lhs()) || couldBeString(b->rhs()); } const Expr *evaled = maybeEval(e); if (evaled != e && evaled->op() == TO_LITERAL) // do not go too deep return couldBeString(evaled); return false; } const Expr *CheckerVisitor::maybeEval(const Expr *e, bool allow_external) { int32_t dummy = -1; return maybeEval(e, dummy, allow_external); } const Expr *CheckerVisitor::maybeEval(const Expr *e, int32_t &evalId, bool allow_external) { std::unordered_set visited; return maybeEval(e, evalId, visited, allow_external); } const Expr *CheckerVisitor::maybeEval(const Expr *e, int32_t &evalId, std::unordered_set &visited, bool allow_external) { if (visited.find(e) != visited.end()) return e; visited.emplace(e); e = deparenStatic(e); const ValueRef *v = findValueForExpr(e); if (!v) { return e; } evalId = v->evalIndex; if (v->hasValue()) { if (!allow_external && v->expression && v->expression->op() == TO_EXTERNAL_VALUE) return e; // If this variable was initialized from another variable (aliased) and that source // variable has been reassigned since, stop evaluating the expression chain. // Example: let a = null; let b = a; a = 123; return b ?? 2 if (v->origin && v->expression && v->expression->op() == TO_ID) { if (v->origin->evalIndex != v->originEvalIndex) { return e; } } return maybeEval(v->expression, evalId, visited, allow_external); } else { return e; } } const char *CheckerVisitor::findFieldName(const Expr *e) { if (e->op() == TO_ID) return e->asId()->name(); if (e->op() == TO_GETFIELD) return e->asGetField()->fieldName(); if (e->op() == TO_CALL) return findFieldName(e->asCallExpr()->callee()); const ValueRef *v = findValueForExpr(e); if (v && v->expression && v->expression->op() == TO_FUNCTION) { return v->expression->asFunctionExpr()->name(); } return ""; } int32_t CheckerVisitor::computeNameLength(const GetFieldExpr *acc) { int32_t size = computeNameLength(acc->receiver()); if (size < 0) { return size; } size += 1; size += strlen(acc->fieldName()); return size; } void CheckerVisitor::computeNameRef(const GetFieldExpr *access, char *b, int32_t &ptr, int32_t size) { computeNameRef(access->receiver(), b, ptr, size); b[ptr++] = '.'; int32_t l = snprintf(&b[ptr], size - ptr, "%s", access->fieldName()); ptr += l; } const ExternalValueExpr *CheckerVisitor::findExternalValue(const Expr *e) { const Expr *ee = maybeEval(e, true); if (ee->op() == TO_EXTERNAL_VALUE) return static_cast(ee); char buffer[128] = { 0 }; const char *name = computeNameRef(ee, buffer, sizeof buffer); if (!name) return nullptr; const ValueRef *v = findValueInScopes(name); if (!v || !v->hasValue()) return nullptr; const Expr *expr = maybeEval(v->expression, true); if (expr->op() == TO_EXTERNAL_VALUE) return static_cast(expr); return nullptr; } const FunctionInfo *CheckerVisitor::findFunctionInfo(const Expr *e, bool &isCtor) { const Expr *ee = maybeEval(e); if (ee->op() == TO_FUNCTION) { return functionInfoMap[ee->asFunctionExpr()]; } char buffer[128] = { 0 }; const char *name = computeNameRef(ee, buffer, sizeof buffer); if (!name) return nullptr; const ValueRef *v = findValueInScopes(name); if (!v || !v->hasValue()) return nullptr; const Expr *expr = maybeEval(v->expression); if (expr->op() == TO_FUNCTION) { return functionInfoMap[expr->asFunctionExpr()]; } else if (expr->op() == TO_CLASS) { const ClassExpr *klass = expr->asClassExpr(); isCtor = true; if (FunctionExpr *ctor = klass->findConstructor()) return functionInfoMap[ctor]; } return nullptr; } void CheckerVisitor::applyKnownInvocationToScope(const ValueRef *value) { const FunctionInfo *info = nullptr; if (value->hasValue()) { const Expr *expr = maybeEval(value->expression); assert(expr != nullptr); if (expr->op() == TO_FUNCTION) { info = functionInfoMap[expr->asFunctionExpr()]; } else if (expr->op() == TO_CLASS) { const ClassExpr *klass = expr->asClassExpr(); if (FunctionExpr *ctor = klass->findConstructor()) info = functionInfoMap[ctor]; } else if (expr->op() == TO_TABLE) { // Tables don't have function info } else if (expr->op() == TO_EXTERNAL_VALUE) { applyUnknownInvocationToScope(); return; } } if (!info) { // probably it is class constructor return; } for (auto s : info->modifiable) { VarScope *scope = currentScope->findScope(s.owner); if (!scope) continue; auto it = scope->symbols.find(s.name); if (it != scope->symbols.end()) { if (it->second->isConstant()) continue; if (currentInfo) { currentInfo->addModifiable(it->first, it->second->info->ownedScope->owner); } it->second->kill(); } } } void CheckerVisitor::applyUnknownInvocationToScope() { VarScope *s = currentScope; while (s) { auto &symbols = s->symbols; for (auto &sym : symbols) { if (sym.second->isConstant()) continue; if (currentInfo) { currentInfo->addModifiable(sym.first, sym.second->info->ownedScope->owner); } sym.second->kill(); } s = s->parent; } } void CheckerVisitor::applyCallToScope(const CallExpr *call) { const Expr *callee = deparenStatic(call->callee()); if (callee->op() == TO_ID) { // -V522 const Id *calleeId = callee->asId(); //const NameRef ref(nullptr, calleeId->name()); const ValueRef *value = findValueInScopes(calleeId->name()); if (value) { applyKnownInvocationToScope(value); } else { // unknown invocation by pure Id points to some external // function which could not modify any scoped value } } else if (callee->op() == TO_GETFIELD) { char buffer[128] = { 0 }; const char *ref = computeNameRef(callee, buffer, sizeof buffer); const ValueRef *value = findValueInScopes(ref); if (value) { applyKnownInvocationToScope(value); } else if (!ref || strcmp(ref, double_colon_str) != 0) { // we don't know what exactly is being called so assume the most conservative case applyUnknownInvocationToScope(); } } else { // unknown invocation so everything could be modified applyUnknownInvocationToScope(); } } void CheckerVisitor::pushFunctionScope(VarScope *functionScope, const FunctionExpr *decl) { FunctionInfo *info = functionInfoMap[decl]; if (!info) { info = makeFunctionInfo(decl, currentScope->owner); functionInfoMap[decl] = info; } currentScope = functionScope; } void CheckerVisitor::declareSymbol(const char *nameRef, ValueRef *v) { currentScope->symbols[nameRef] = v; } void CheckerVisitor::visitParamDecl(ParamDecl *p) { Visitor::visitParamDecl(p); const Expr *dv = p->defaultValue(); SymbolInfo *info = makeSymbolInfo(SK_PARAM); ValueRef *v = makeValueRef(info); v->state = VRS_UNKNOWN; v->expression = nullptr; info->declarator.p = p; info->ownedScope = currentScope; info->used = p->isVararg(); if (dv && dv->op() == TO_LITERAL) { if (dv->asLiteral()->kind() == LK_NULL) { v->flagsPositive |= RT_NULL; } } declareSymbol(p->name(), v); assert(currentInfo); if (!isEffectsGatheringPass) currentInfo->parameters.push_back(normalizeParamName(p->name())); } void CheckerVisitor::visitVarDecl(VarDecl *decl) { Visitor::visitVarDecl(decl); SymbolInfo *info = makeSymbolInfo(decl->isAssignable() ? SK_VAR : SK_BINDING); ValueRef *v = makeValueRef(info); const Expr *init = decl->initializer(); const Expr *origInit = deparenStatic(init); init = maybeEval(init, true); if (decl->isDestructured()) { v->state = VRS_UNKNOWN; v->expression = nullptr; } else { v->state = VRS_INITIALIZED; if (init) { v->expression = init; } else { v->expression = &nullValue; v->flagsPositive |= RT_NULL; } } if (init) { if (init->op() == TO_LITERAL) { if (init->asLiteral()->kind() == LK_NULL) { v->flagsPositive |= RT_NULL; } } } // Track aliasing: if initialized from a variable, store reference for dynamic flag lookup. // This allows checking the source's current flags even if they change after this declaration. if (origInit && origInit->op() == TO_ID) { const ValueRef *vr = findValueForExpr(origInit); if (vr) { v->flagsPositive = vr->flagsPositive; v->flagsNegative = vr->flagsNegative; v->origin = vr; v->originEvalIndex = vr->evalIndex; } } info->declarator.v = decl; info->ownedScope = currentScope; declareSymbol(decl->name(), v); astValues[decl] = v; } void CheckerVisitor::visitConstDecl(ConstDecl *cnst) { if (cnst->isGlobal()) { storeGlobalDeclaration(cnst->name(), cnst); } SymbolInfo *info = makeSymbolInfo(SK_CONST); ValueRef *v = makeValueRef(info); v->state = VRS_INITIALIZED; v->expression = cnst->value(); info->declarator.c = cnst; info->ownedScope = currentScope; info->used = cnst->isGlobal(); declareSymbol(cnst->name(), v); Visitor::visitConstDecl(cnst); } void CheckerVisitor::visitEnumDecl(EnumDecl *enm) { if (enm->isGlobal()) { storeGlobalDeclaration(enm->name(), enm); } SymbolInfo *info = makeSymbolInfo(SK_ENUM); ValueRef *ev = makeValueRef(info); ev->state = VRS_DECLARED; ev->expression = nullptr; info->declarator.e = enm; info->ownedScope = currentScope; info->used = enm->isGlobal(); declareSymbol(enm->name(), ev); for (auto &c : enm->consts()) { SymbolInfo *constInfo = makeSymbolInfo(SK_ENUM_CONST); ValueRef *cv = makeValueRef(constInfo); cv->state = VRS_INITIALIZED; cv->expression = c.val; constInfo->declarator.ec = &c; constInfo->ownedScope = currentScope; constInfo->used = enm->isGlobal(); const char *fqn = enumFqn(arena, enm->name(), c.id); declareSymbol(fqn, cv); c.val->visit(this); } } void CheckerVisitor::checkDestructuredVarDefault(VarDecl *var) { const Expr *def = var->initializer(); def = maybeEval(def, true); if (def) { auto vv = astValues[var]; assert(vv); vv->state = VRS_INITIALIZED; vv->expression = def; } else { report(var, DiagnosticsId::DI_MISSING_DESTRUCTURED_VALUE, var->name()); } } void CheckerVisitor::visitDestructuringDecl(DestructuringDecl *d) { checkAccessNullable(d); Visitor::visitDestructuringDecl(d); if (auto v = findValueForExpr(d->initExpression()); v && v->hasValue() && v->expression) { if (v->expression->op() == TO_EXTERNAL_VALUE) { const auto &init = v->expression->asExternal()->value(); if (sq_isnull(init)) { for (auto var : d->declarations()) { checkDestructuredVarDefault(var); } } else if (sq_istable(init)) { if (d->type() == DT_TABLE) { auto table = _table(init); for (auto var : d->declarations()) { SQObjectPtr val; if (table->GetStr(var->name(), strlen(var->name()), val)) { auto vv = astValues[var]; assert(vv); vv->state = VRS_INITIALIZED; vv->expression = addExternalValue(val, var)->expression; } else { checkDestructuredVarDefault(var); } } } else { report(d, DiagnosticsId::DI_DESTRUCTURED_TYPE_MISMATCH, "table"); } } else if (sq_isclass(init)) { if (d->type() == DT_TABLE) { auto klass = _class(init); for (auto var : d->declarations()) { SQObjectPtr key(_ctx.getVm(), var->name()); SQObjectPtr val; if (klass->Get(key, val)) { auto vv = astValues[var]; assert(vv); vv->state = VRS_INITIALIZED; vv->expression = addExternalValue(val, var)->expression; } else { checkDestructuredVarDefault(var); } } } else { report(d, DiagnosticsId::DI_DESTRUCTURED_TYPE_MISMATCH, "class"); } } else if (sq_isinstance(init)) { if (d->type() == DT_TABLE) { auto inst = _instance(init); for (auto var : d->declarations()) { SQObjectPtr key(_ctx.getVm(), var->name()); SQObjectPtr val; if (inst->Get(key, val)) { auto vv = astValues[var]; assert(vv); vv->state = VRS_INITIALIZED; vv->expression = addExternalValue(val, var)->expression; } else { checkDestructuredVarDefault(var); } } } else { report(d, DiagnosticsId::DI_DESTRUCTURED_TYPE_MISMATCH, "instance"); } } else if (sq_isarray(init)) { if (d->type() == DT_ARRAY) { auto array = _array(init); SQInteger index = 0; for (auto var : d->declarations()) { SQObjectPtr val; if (array->Get(index, val)) { auto vv = astValues[var]; assert(vv); vv->state = VRS_INITIALIZED; vv->expression = addExternalValue(val, var)->expression; } else { checkDestructuredVarDefault(var); } index++; } } else { report(d, DiagnosticsId::DI_DESTRUCTURED_TYPE_MISMATCH, "array"); } } else { report(d, DiagnosticsId::DI_DESTRUCTURED_TYPE_MISMATCH, GetTypeName(init)); } } else if (v->expression->op() == TO_TABLE) { // TODO: check table destructuring } else if (v->expression->op() == TO_ARRAY) { // TODO: check array destructuring } } } void CheckerVisitor::visitImportStatement(ImportStmt *import) { if (import->slots.empty()) { // Handle whole-module imports ImportInfo *importInfo = (ImportInfo *)arena->allocate(sizeof(ImportInfo)); importInfo->line = import->lineStart(); importInfo->column = import->moduleAlias ? import->aliasCol : import->nameCol; importInfo->name = import->moduleAlias ? import->moduleAlias : import->moduleName; // Find an external value auto-collected from bindings in analyze() ValueRef *existing = findValueInScopes(importInfo->name); assert(existing); SymbolInfo *info = makeSymbolInfo(SK_IMPORT); ValueRef *v = makeValueRef(info); v->state = VRS_INITIALIZED; v->expression = existing->expression; //-V522 info->declarator.imp = importInfo; info->ownedScope = currentScope; // Warning! This redeclares symbol with the same name // This is a temporary solution and will be fixed separately declareSymbol(importInfo->name, v); } else { for (const SQModuleImportSlot &slot : import->slots) { if (strcmp(slot.name, "*") == 0) continue; ImportInfo *importInfo = (ImportInfo *)arena->allocate(sizeof(ImportInfo)); importInfo->line = slot.line; importInfo->column = slot.column; importInfo->name = slot.alias ? slot.alias : slot.name; // Find an external value auto-collected from bindings in analyze() ValueRef *existing = findValueInScopes(importInfo->name); assert(existing); SymbolInfo *info = makeSymbolInfo(SK_IMPORT); ValueRef *v = makeValueRef(info); v->state = VRS_INITIALIZED; v->expression = existing->expression; //-V522 info->declarator.imp = importInfo; info->ownedScope = currentScope; // Warning! This redeclares symbol with the same name // This is a temporary solution and will be fixed separately declareSymbol(importInfo->name, v); } } Visitor::visitImportStatement(import); } ValueRef* CheckerVisitor::addExternalValue(const SQObject &val, const Node *location) { SymbolInfo *info = makeSymbolInfo(SK_EXTERNAL_BINDING); ValueRef *v = makeValueRef(info); // Create ExternalValueExpr with span from location (if provided) or invalid span SourceSpan spanFromLoc = location ? location->sourceSpan() : SourceSpan::invalid(); ExternalValueExpr *ev = new(arena) ExternalValueExpr(val, spanFromLoc); externalValues.push_back(ev); info->declarator.ev = ev; v->state = VRS_INITIALIZED; v->expression = ev; if (sq_isnull(val)) v->flagsPositive |= RT_NULL; // TODO: could create LiteralExpr for some values return v; } void CheckerVisitor::analyze(RootBlock *root, const HSQOBJECT *bindings) { assert(currentScope == nullptr); VarScope rootScope(nullptr, nullptr); currentScope = &rootScope; if (bindings && sq_istable(*bindings)) { SQTable *table = _table(*bindings); SQInteger idx = 0; SQObjectPtr pos(idx), key, val; while ((idx = table->Next(false, pos, key, val)) >= 0) { if (sq_isstring(key)) { const char *name = _string(key)->_val; declareSymbol(name, addExternalValue(val, root)); } pos._unVal.nInteger = idx; } } root->visit(this); rootScope.parent = nullptr; currentScope = nullptr; } } ================================================ FILE: squirrel/compiler/static_analyzer/checker_visitor.h ================================================ #pragma once #include "compiler/compilationcontext.h" #include "node_equal_checker.h" #include "modification_checker.h" #include "ast_helpers.h" #include "function_info.h" #include "value_ref.h" #include namespace SQCompilation { struct VarScope; struct BreakableScope; struct IdLocation { const char *filename; int32_t line, column; bool diagSilenced; }; class PredicateCheckerVisitor : public Visitor { bool deepCheck; bool result; const Node *checkee; protected: NodeEqualChecker equalChecker; PredicateCheckerVisitor(bool deep) : deepCheck(deep), result(false), checkee(nullptr) {} virtual bool doCheck(const Node *checkee, Node *n) const = 0; public: void visitNode(Node *n) { if (doCheck(checkee, n)) { result = true; return; } if (deepCheck) n->visitChildren(this); } bool check(const Node *toCheck, Node *tree) { result = false; checkee = toCheck; tree->visit(this); // -V522 return result; } }; class CheckModificationVisitor : public PredicateCheckerVisitor { protected: bool doCheck(const Node *checkee, Node *n) const { TreeOp op = n->op(); if (op == TO_ASSIGN || (TO_PLUSEQ <= op && op <= TO_MODEQ)) { BinExpr *bin = static_cast(n); return equalChecker.check(checkee, bin->lhs()); } if (op == TO_INC) { IncExpr *inc = static_cast(n); return equalChecker.check(checkee, inc->argument()); } return false; } public: CheckModificationVisitor() : PredicateCheckerVisitor(false) {} }; class ExistsChecker : public PredicateCheckerVisitor { protected: bool doCheck(const Node *checkee, Node *n) const { return equalChecker.check(checkee, n); } public: ExistsChecker() : PredicateCheckerVisitor(true) {} }; class CheckerVisitor : public Visitor { friend struct VarScope; friend struct BreakableScope; friend class FunctionReturnTypeEvaluator; const int32_t functionComplexityThreshold = 45; // get threshold complexity from command line args const int32_t statementComplexityThreshold = 23; const int32_t statementSimilarityThreshold = 33; SQCompilationContext &_ctx; NodeEqualChecker _equalChecker; void report(const Node *n, int32_t id, ...); void reportImportSlot(int line, int column, const char *name); void checkKeyNameMismatch(const Expr *key, const Expr *expr); void checkAlwaysTrueOrFalse(const Expr *expr); void checkIdUsed(const Id *id, const Node *p, ValueRef *v); void reportIfCannotBeNull(const Expr *checkee, const Expr *n, const char *loc); void reportModifyIfContainer(const Expr *e, const Expr *mod); void checkForgotSubst(const LiteralExpr *l); void checkContainerModification(const UnExpr *expr); void checkAndOrPriority(const BinExpr *expr); void checkBitwiseParenBool(const BinExpr *expr); void checkNullCoalescingPriority(const BinExpr *expr); void checkAssignmentToItself(const BinExpr *expr); void checkSameOperands(const BinExpr *expr); void checkAlwaysTrueOrFalse(const BinExpr *expr); void checkDeclarationInArith(const BinExpr *expr); void checkIntDivByZero(const BinExpr *expr); void checkPotentiallyNullableOperands(const BinExpr *expr); void checkBitwiseToBool(const BinExpr *expr); void checkCompareWithBool(const BinExpr *expr); void checkRelativeCompareWithBool(const BinExpr *expr); void checkCopyOfExpression(const BinExpr *expr); void checkConstInBoolExpr(const BinExpr *expr); void checkShiftPriority(const BinExpr *expr); void checkCompareWithContainer(const BinExpr *expr); void checkBoolToStrangePosition(const BinExpr *expr); void checkNewSlotNameMatch(const BinExpr *expr); void checkPlusString(const BinExpr *expr); void checkNewGlobalSlot(const BinExpr *); void checkUselessNullC(const BinExpr *); void checkCannotBeNull(const BinExpr *); void checkCanBeSimplified(const BinExpr *expr); void checkRangeCheck(const BinExpr *expr); void checkParamAssignInLambda(const BinExpr *expr); void checkAlwaysTrueOrFalse(const TerExpr *expr); void checkTernaryPriority(const TerExpr *expr); void checkSameValues(const TerExpr *expr); void checkCanBeSimplified(const TerExpr *expr); void checkExtendToAppend(const CallExpr *callExpr); void checkMergeEmptyTable(const CallExpr *callExpr); void checkEmptyArrayResize(const CallExpr *callExpr); void checkAlreadyRequired(const CallExpr *callExpr); void checkCallNullable(const CallExpr *callExpr); void checkPersistCall(const CallExpr *callExpr); void checkForbiddenCall(const CallExpr *callExpr); void checkCallFromRoot(const CallExpr *callExpr); void checkForbiddenParentDir(const CallExpr *callExpr); void checkFormatArguments(const CallExpr *callExpr); void checkArguments(const CallExpr *callExpr); void checkContainerModification(const CallExpr *expr); void checkUnwantedModification(const CallExpr *expr); void checkCannotBeNull(const CallExpr *expr); void checkBooleanLambda(const CallExpr *expr); void checkCallbackReturnValue(const CallExpr *expr); void checkCallbackShouldNotReturn(const CallExpr *expr); void checkBoolIndex(const GetSlotExpr *expr); void checkNullableIndex(const GetSlotExpr *expr); void checkGlobalAccess(const GetFieldExpr *expr); void checkAccessFromStatic(const GetFieldExpr *expr); void checkExternalField(const GetFieldExpr *expr); bool hasDynamicContent(const SQObject &container); bool findIfWithTheSameCondition(const Expr * condition, const IfStatement * elseNode, const Expr *&duplicated) { if (_equalChecker.check(condition, elseNode->condition())) { duplicated = elseNode->condition(); return true; } const Statement *elseB = unwrapSingleBlock(elseNode->elseBranch()); if (elseB && elseB->op() == TO_IF) { return findIfWithTheSameCondition(condition, static_cast(elseB), duplicated); } return false; } const char *normalizeParamName(const char *name, char *buffer = nullptr); int32_t normalizeParamNameLength(const char *n); void checkDuplicateSwitchCases(SwitchStatement *swtch); void checkDuplicateIfBranches(IfStatement *ifStmt); void checkDuplicateIfConditions(IfStatement *ifStmt); void checkSuspiciousFormatting(const Statement *body, const Statement *stmt); void checkSuspiciousFormattingOfStetementSequence(const Statement* prev, const Statement* cur); bool onlyEmptyStatements(int32_t start, const ArenaVector &statements) { for (int32_t i = start; i < statements.size(); ++i) { Statement *stmt = statements[i]; if (stmt->op() != TO_EMPTY) return false; } return true; } bool existsInTree(const Expr *toSearch, Expr *tree) const { return ExistsChecker().check(toSearch, tree); } bool indexChangedInTree(Expr *index) const { return ModificationChecker().check(index); } bool checkModification(Expr *key, Node *tree) { return CheckModificationVisitor().check(key, tree); } bool shouldCallResultBeUtilized(const char *name, const CallExpr *call); void checkUnterminatedLoop(LoopStatement *loop); void checkVariableMismatchForLoop(ForStatement *loop); void checkEmptyWhileBody(WhileStatement *loop); void checkEmptyThenBody(IfStatement *stmt); void checkForgottenDo(const Block *block); void checkUnreachableCode(const Block *block); void checkAssignedTwice(const Block *b); void checkFunctionSimilarity(const Block *b); void checkFunctionSimilarity(const TableExpr *table); void checkFunctionPairSimilarity(const FunctionExpr *f1, const FunctionExpr *f2); void checkAssignExpressionSimilarity(const Block *b); void checkUnutilizedResult(const ExprStatement *b); void checkNullableContainer(const ForeachStatement *loop); void checkMissedBreak(const SwitchStatement *swtch); const char *findSlotNameInStack(const Node *); void checkFunctionReturns(FunctionExpr *func); void checkAccessNullable(const DestructuringDecl *d); void checkAccessNullable(const AccessExpr *acc); void checkEnumConstUsage(const GetFieldExpr *acc); enum StackSlotType { SST_NODE, SST_TABLE_MEMBER }; struct StackSlot { StackSlotType sst; union { const Node *n; const TableMember *member; }; }; std::vector nodeStack; VarScope *currentScope; BreakableScope *breakScope; FunctionInfo *makeFunctionInfo(const FunctionExpr *d, const FunctionExpr *o) { void *mem = arena->allocate(sizeof(FunctionInfo)); return new(mem) FunctionInfo(d, o); } ValueRef *makeValueRef(SymbolInfo *info) { void *mem = arena->allocate(sizeof(ValueRef)); return new (mem) ValueRef(info, currentScope->evalId); } SymbolInfo *makeSymbolInfo(SymbolKind kind) { void *mem = arena->allocate(sizeof(SymbolInfo)); return new (mem) SymbolInfo(kind); } std::unordered_map functionInfoMap; std::unordered_set requiredModules; std::unordered_set persistedKeys; std::unordered_map astValues; std::vector externalValues; Arena *arena; FunctionInfo *currentInfo; void declareSymbol(const char *name, ValueRef *v); void pushFunctionScope(VarScope *functionScope, const FunctionExpr *decl); void applyCallToScope(const CallExpr *call); void applyBinaryToScope(const BinExpr *bin); void applyAssignmentToScope(const BinExpr *bin); void applyAssignEqToScope(const BinExpr *bin); int32_t computeNameLength(const GetFieldExpr *access); void computeNameRef(const GetFieldExpr *access, char *b, int32_t &ptr, int32_t size); int32_t computeNameLength(const Expr *e); void computeNameRef(const Expr *lhs, char *buffer, int32_t &ptr, int32_t size); const char *computeNameRef(const Expr *lhs, char *buffer, size_t bufferSize); ValueRef *findValueInScopes(const char *ref); void applyKnownInvocationToScope(const ValueRef *ref); void applyUnknownInvocationToScope(); const ExternalValueExpr *findExternalValue(const Expr *e); const FunctionInfo *findFunctionInfo(const Expr *e, bool &isCtor); void setValueFlags(const Expr *lvalue, unsigned pf, unsigned nf); const ValueRef *findValueForExpr(const Expr *e); const Expr *maybeEval(const Expr *e, int32_t &evalId, std::unordered_set &visited, bool allow_external = false); const Expr *maybeEval(const Expr *e, int32_t &evalId, bool allow_external = false); const Expr *maybeEval(const Expr *e, bool allow_external = false); const char *findFieldName(const Expr *e); bool isSafeAccess(const AccessExpr *acc); bool isPotentiallyNullable(const Expr *e); bool isPotentiallyNullable(const Expr *e, std::unordered_set &visited); bool couldBeString(const Expr *e); void visitBinaryBranches(Expr *lhs, Expr *rhs, bool inv); void speculateIfConditionHeuristics(const Expr *cond, VarScope *thenScope, VarScope *elseScope, bool inv = false); void speculateIfConditionHeuristics(const Expr *cond, VarScope *thenScope, VarScope *elseScope, std::unordered_set &visited, int32_t evalId, unsigned flags, bool inv); bool detectTypeOfPattern(const Expr *expr, const Expr *&r_checkee, const LiteralExpr *&r_lit); bool detectNullCPattern(TreeOp op, const Expr *expr, const Expr *&checkee); void checkAssertCall(const CallExpr *call); const char *extractFunctionName(const CallExpr *call); LiteralExpr trueValue, falseValue, nullValue; bool isEffectsGatheringPass; void putIntoGlobalNamesMap(std::unordered_map> &map, enum DiagnosticsId diag, const char *name, const Node *d); void storeGlobalDeclaration(const char *name, const Node *d); void storeGlobalUsage(const char *name, const Node *d); public: CheckerVisitor(SQCompilationContext &ctx) : _ctx(ctx) , arena(ctx.arena()) , currentInfo(nullptr) , currentScope(nullptr) , breakScope(nullptr) , trueValue(SourceSpan::invalid(), true) , falseValue(SourceSpan::invalid(), false) , nullValue(SourceSpan::invalid()) , isEffectsGatheringPass(false) {} ~CheckerVisitor(); void visitNode(Node *n); void visitLiteralExpr(LiteralExpr *l); void visitId(Id *id); void visitUnExpr(UnExpr *expr); void visitBinExpr(BinExpr *expr); void visitTerExpr(TerExpr *expr); void visitIncExpr(IncExpr *expr); void visitCallExpr(CallExpr *expr); void visitGetFieldExpr(GetFieldExpr *expr); void visitGetSlotExpr(GetSlotExpr *expr); void visitExprStatement(ExprStatement *stmt); void visitBlock(Block *b); void visitForStatement(ForStatement *loop); void visitForeachStatement(ForeachStatement *loop); void visitWhileStatement(WhileStatement *loop); void visitDoWhileStatement(DoWhileStatement *loop); void visitIfStatement(IfStatement *ifstmt); void visitBreakStatement(BreakStatement *breakStmt); void visitContinueStatement(ContinueStatement *continueStmt); void visitSwitchStatement(SwitchStatement *swtch); void visitTryStatement(TryStatement *tryStmt); void visitFunctionExpr(FunctionExpr *func); void visitTableExpr(TableExpr *table); void visitClassExpr(ClassExpr *klass); void visitParamDecl(ParamDecl *p); void visitVarDecl(VarDecl *decl); void visitConstDecl(ConstDecl *cnst); void visitEnumDecl(EnumDecl *enm); void visitDestructuringDecl(DestructuringDecl *decl); void visitImportStatement(ImportStmt *import); ValueRef* addExternalValue(const SQObject &val, const Node *location); void checkDestructuredVarDefault(VarDecl *var); void analyze(RootBlock *root, const HSQOBJECT *bindings); }; } ================================================ FILE: squirrel/compiler/static_analyzer/config.cpp ================================================ #include "config.h" #include "keyValueFile.h" namespace SQCompilation { bool do_report_missing_modules = false; std::vector function_forbidden; std::vector format_function_name; std::vector function_can_return_string; std::vector function_should_return_bool_prefix; std::vector function_should_return_something_prefix; std::vector function_result_must_be_utilized; std::vector function_can_return_null; std::vector function_takes_boolean_lambda; std::vector function_requires_result_from_callback; std::vector function_ignores_callback_result; std::vector function_forbidden_parent_dir; std::vector function_modifies_object; std::vector function_must_be_called_from_root; void resetAnalyzerConfig() { function_forbidden = { }; format_function_name = { "prn", "print", "form", "fmt", "log", "dbg", "assert", }; function_can_return_string = { "subst", "concat", "tostring", "toupper", "tolower", "slice", "trim", "join", "format", "replace" }; function_should_return_bool_prefix = { "has", "Has", "have", "Have", "should", "Should", "need", "Need", "is", "Is", "was", "Was", "will", "Will", "contains", "match", "startswith", "endswith" }; function_should_return_something_prefix = { "get", "Get" }; function_result_must_be_utilized = { "__merge", "indexof", "findindex", "findvalue", "len", "reduce", "tostring", "tointeger", "tofloat", "slice", "tolower", "toupper" }; function_can_return_null = { "indexof", "findindex", "findvalue", "require_optional" }; function_takes_boolean_lambda = { "findvalue", "findindex", "filter" }; function_ignores_callback_result = { "each", "mutate", }; function_requires_result_from_callback = { "filter", "findindex", "findvalue", "map", "reduce", "sort", "apply", "modify", }; function_forbidden_parent_dir = { "require", "require_optional", }; function_modifies_object = { "extend", "append", "__update", "insert", "apply", "clear", "sort", "reverse", "resize", "rawdelete", "rawset", "remove" }; function_must_be_called_from_root = { "keepref" }; } bool loadAnalyzerConfigFile(const char *configFile) { KeyValueFile config; if (!config.loadFromFile(configFile)) { return false; } return loadAnalyzerConfigFile(config); } bool loadAnalyzerConfigFile(const KeyValueFile &config) { do_report_missing_modules = config.getBool("report_missing_modules", false); for (auto && v : config.getValuesList("forbidden_function")) function_forbidden.push_back(v); for (auto && v : config.getValuesList("function_can_return_null")) function_can_return_null.push_back(v); for (auto && v : config.getValuesList("function_result_must_be_utilized")) function_result_must_be_utilized.push_back(v); for (auto && v : config.getValuesList("function_can_return_string")) function_can_return_string.push_back(v); for (auto && v : config.getValuesList("function_should_return_bool_prefix")) function_should_return_bool_prefix.push_back(v); for (auto && v : config.getValuesList("function_should_return_something_prefix")) function_should_return_something_prefix.push_back(v); for (auto && v : config.getValuesList("function_forbidden_parent_dir")) function_forbidden_parent_dir.push_back(v); for (auto && v : config.getValuesList("function_modifies_object")) function_modifies_object.push_back(v); for (auto && v : config.getValuesList("function_must_be_called_from_root")) function_must_be_called_from_root.push_back(v); for (auto && v : config.getValuesList("function_requires_result_from_callback")) function_requires_result_from_callback.push_back(v); for (auto && v : config.getValuesList("function_ignores_callback_result")) function_ignores_callback_result.push_back(v); return true; } } ================================================ FILE: squirrel/compiler/static_analyzer/config.h ================================================ #pragma once #include #include class KeyValueFile; namespace SQCompilation { extern bool do_report_missing_modules; extern std::vector function_can_return_string; extern std::vector function_should_return_bool_prefix; extern std::vector function_should_return_something_prefix; extern std::vector function_result_must_be_utilized; extern std::vector function_can_return_null; extern std::vector function_takes_boolean_lambda; extern std::vector function_requires_result_from_callback; extern std::vector function_ignores_callback_result; extern std::vector format_function_name; extern std::vector function_forbidden; extern std::vector function_forbidden_parent_dir; extern std::vector function_modifies_object; extern std::vector function_must_be_called_from_root; void resetAnalyzerConfig(); bool loadAnalyzerConfigFile(const KeyValueFile &configBlk); bool loadAnalyzerConfigFile(const char *configFile); } ================================================ FILE: squirrel/compiler/static_analyzer/function_info.h ================================================ #pragma once namespace SQCompilation { struct FunctionInfo { FunctionInfo(const FunctionExpr *d, const FunctionExpr *o) : declaration(d), owner(o) {} ~FunctionInfo() = default; struct Modifiable { const FunctionExpr *owner; const char *name; }; const FunctionExpr *owner; std::vector modifiable; const FunctionExpr *declaration; std::vector parameters; void joinModifiable(const FunctionInfo *other); void addModifiable(const char *name, const FunctionExpr *o); }; } ================================================ FILE: squirrel/compiler/static_analyzer/function_ret_type_eval.cpp ================================================ #include "function_ret_type_eval.h" #include "naming.h" #include "checker_visitor.h" namespace SQCompilation { StmtEvalResult FunctionReturnTypeEvaluator::evalStmt(const Statement *n) { switch (n->op()) { case TO_RETURN: return evalReturn(static_cast(n)); case TO_THROW: return evalThrow(static_cast(n)); case TO_FOR: case TO_FOREACH: case TO_WHILE: case TO_DOWHILE: return evalLoop(static_cast(n)); case TO_IF: return evalIf(static_cast(n)); case TO_SWITCH: return evalSwitch(static_cast(n)); case TO_BLOCK: return evalBlock(static_cast(n)); case TO_TRY: return evalTry(static_cast(n)); case TO_BREAK: case TO_CONTINUE: return StmtEvalResult::fallsThrough(); // Control flow, but no return default: return StmtEvalResult::fallsThrough(); // Expression statements, declarations, etc. } } unsigned FunctionReturnTypeEvaluator::evalLiteral(const LiteralExpr *lit) { switch (lit->kind()) { case LK_STRING: return RT_STRING; case LK_NULL: return RT_NULL; case LK_BOOL: return RT_BOOL; default: return RT_NUMBER; } } unsigned FunctionReturnTypeEvaluator::evalExpr(const Expr *expr) { switch (expr->op()) { case TO_LITERAL: return evalLiteral(static_cast(expr)); case TO_OROR: case TO_ANDAND: case TO_NULLC: return evalCompoundBin(static_cast(expr)); case TO_NE: case TO_EQ: case TO_GE: case TO_GT: case TO_LE: case TO_LT: case TO_INSTANCEOF: case TO_IN: case TO_NOT: return RT_BOOL; case TO_ADD: return evalAddExpr(static_cast(expr)); case TO_MOD: { const BinExpr *b = static_cast(expr); if (canBeString(b->rhs())) // this special pattern 'o % "something"' return RT_UNRECOGNIZED; } // -V796 fall through case TO_SUB: case TO_MUL: case TO_DIV: case TO_NEG: case TO_BNOT: case TO_3CMP: case TO_AND: case TO_OR: case TO_XOR: case TO_SHL: case TO_SHR: case TO_USHR: case TO_INC: return RT_NUMBER; case TO_CALL: return evalCall(expr->asCallExpr()); case TO_TABLE: return RT_TABLE; case TO_CLASS: return RT_CLASS; case TO_FUNCTION: return RT_CLOSURE; case TO_ARRAY: return RT_ARRAY; case TO_PAREN: return evalExpr(static_cast(expr)->argument()); case TO_TERNARY: { const TerExpr *ter = static_cast(expr); return evalExpr(ter->b()) | evalExpr(ter->c()); } case TO_ID: return evalId(expr->asId()); case TO_GETFIELD: { unsigned flags = evalGetField(expr->asGetField()); if (expr->asAccessExpr()->isNullable()) flags |= RT_NULL; return flags; } case TO_GETSLOT: if (expr->asAccessExpr()->isNullable()) return RT_NULL; return 0; case TO_ASSIGN: return RT_NOTHING; default: if (canBeString(expr)) return RT_STRING; else return RT_UNRECOGNIZED; } } unsigned FunctionReturnTypeEvaluator::evalAddExpr(const BinExpr *expr) { assert(expr->op() == TO_ADD); unsigned lhsFlags = evalExpr(expr->lhs()); unsigned rhsFlags = evalExpr(expr->rhs()); if ((lhsFlags & RT_STRING) || (rhsFlags & RT_STRING)) { // concat with string is casted to string return RT_STRING; } else if ((lhsFlags | rhsFlags) & RT_NUMBER) { return RT_NUMBER; } else { return lhsFlags | rhsFlags; } } StmtEvalResult FunctionReturnTypeEvaluator::evalReturn(const ReturnStatement *ret) { const Expr *arg = ret->argument(); if (arg == nullptr) { return StmtEvalResult::returns(RT_NOTHING); } return StmtEvalResult::returns(evalExpr(arg)); } StmtEvalResult FunctionReturnTypeEvaluator::evalThrow(const ThrowStatement *thrw) { return StmtEvalResult::returns(RT_THROW); } StmtEvalResult FunctionReturnTypeEvaluator::evalIf(const IfStatement *ifStmt) { StmtEvalResult thenResult = evalStmt(ifStmt->thenBranch()); if (!ifStmt->elseBranch()) { return StmtEvalResult::fallsThrough(thenResult.flags); } StmtEvalResult elseResult = evalStmt(ifStmt->elseBranch()); unsigned combinedFlags = thenResult.flags | elseResult.flags; bool allReturn = thenResult.allPathsReturn && elseResult.allPathsReturn; return StmtEvalResult(combinedFlags, allReturn); } StmtEvalResult FunctionReturnTypeEvaluator::evalLoop(const LoopStatement *loop) { StmtEvalResult bodyResult = evalStmt(loop->body()); // Loop may not execute at all, so it never guarantees return return StmtEvalResult::fallsThrough(bodyResult.flags); } StmtEvalResult FunctionReturnTypeEvaluator::evalBlock(const Block *block) { unsigned accumulatedFlags = 0; bool lastReturns = false; for (const Statement *stmt : block->statements()) { if (stmt->op() == TO_EMPTY) continue; StmtEvalResult result = evalStmt(stmt); accumulatedFlags |= result.flags; lastReturns = result.allPathsReturn; } return StmtEvalResult(accumulatedFlags, lastReturns); } StmtEvalResult FunctionReturnTypeEvaluator::evalSwitch(const SwitchStatement *swtch) { unsigned accumulatedFlags = 0; bool allCasesReturn = true; for (auto &c : swtch->cases()) { StmtEvalResult caseResult = evalStmt(c.stmt); accumulatedFlags |= caseResult.flags; if (!caseResult.allPathsReturn) { const Statement *last = unwrapLastStatement(c.stmt); if (!last || last->op() != TO_BREAK) // fall through continue; } allCasesReturn &= caseResult.allPathsReturn; } if (swtch->defaultCase().stmt) { StmtEvalResult defaultResult = evalStmt(swtch->defaultCase().stmt); accumulatedFlags |= defaultResult.flags; allCasesReturn &= defaultResult.allPathsReturn; } else { allCasesReturn = false; } return StmtEvalResult(accumulatedFlags, allCasesReturn); } StmtEvalResult FunctionReturnTypeEvaluator::evalTry(const TryStatement *stmt) { StmtEvalResult tryResult = evalStmt(stmt->tryStatement()); StmtEvalResult catchResult = evalStmt(stmt->catchStatement()); unsigned combinedFlags = tryResult.flags | catchResult.flags; bool allReturn = tryResult.allPathsReturn && catchResult.allPathsReturn; return StmtEvalResult(combinedFlags, allReturn); } unsigned FunctionReturnTypeEvaluator::evalCompoundBin(const BinExpr *expr) { TreeOp op = expr->op(); assert(op == TO_ANDAND || op == TO_OROR || op == TO_NULLC); unsigned lhsFlags = evalExpr(expr->lhs()); unsigned rhsFlags = evalExpr(expr->rhs()); lhsFlags &= ~RT_UNRECOGNIZED; rhsFlags &= ~RT_UNRECOGNIZED; bool hasFuncCall = (lhsFlags | rhsFlags) & RT_FUNCTION_CALL; lhsFlags &= ~RT_FUNCTION_CALL; rhsFlags &= ~RT_FUNCTION_CALL; unsigned resultFlags = 0; if (op != TO_NULLC && (((lhsFlags & RT_BOOL) && (rhsFlags & RT_NUMBER)) || ((rhsFlags & RT_BOOL) && (lhsFlags & RT_NUMBER)))) resultFlags = RT_BOOL; else if (rhsFlags & RT_NULL) resultFlags = RT_NULL; else if ((lhsFlags | rhsFlags) & RT_STRING) resultFlags = RT_STRING; else resultFlags = rhsFlags; if (hasFuncCall) resultFlags |= RT_FUNCTION_CALL; return resultFlags; } unsigned FunctionReturnTypeEvaluator::evalId(const Id *id) { if (nameLooksLikeResultMustBeBoolean(id->name())) return RT_BOOL; else return RT_UNRECOGNIZED; } unsigned FunctionReturnTypeEvaluator::evalGetField(const GetFieldExpr *gf) { if (nameLooksLikeResultMustBeBoolean(gf->fieldName())) return RT_BOOL; else return RT_UNRECOGNIZED; } unsigned FunctionReturnTypeEvaluator::evalCall(const CallExpr *call) { unsigned flags = 0; if (canBeString(call)) { flags |= RT_STRING; } const char *fn = checker->extractFunctionName(call); if (nameLooksLikeResultMustBeBoolean(fn)) { flags |= RT_BOOL; } if (call->isNullable()) flags |= RT_NULL; flags |= RT_FUNCTION_CALL; return flags; } bool FunctionReturnTypeEvaluator::canBeString(const Expr *e) { return checker->couldBeString(e); } } ================================================ FILE: squirrel/compiler/static_analyzer/function_ret_type_eval.h ================================================ #pragma once #include "compiler/ast.h" #include "analyzer_internal.h" namespace SQCompilation { class CheckerVisitor; struct StmtEvalResult { unsigned flags; bool allPathsReturn; StmtEvalResult(unsigned f, bool r) : flags(f), allPathsReturn(r) {} static StmtEvalResult returns(unsigned f) { return StmtEvalResult(f, true); } static StmtEvalResult fallsThrough(unsigned f) { return StmtEvalResult(f, false); } static StmtEvalResult fallsThrough() { return StmtEvalResult(0, false); } }; class FunctionReturnTypeEvaluator { unsigned evalLiteral(const LiteralExpr *l); unsigned evalAddExpr(const BinExpr *expr); unsigned evalExpr(const Expr *expr); unsigned evalCall(const CallExpr *expr); unsigned evalId(const Id *id); unsigned evalGetField(const GetFieldExpr *gf); unsigned evalCompoundBin(const BinExpr *expr); StmtEvalResult evalStmt(const Statement *node); StmtEvalResult evalBlock(const Block *b); StmtEvalResult evalIf(const IfStatement *stmt); StmtEvalResult evalLoop(const LoopStatement *loop); StmtEvalResult evalSwitch(const SwitchStatement *swtch); StmtEvalResult evalReturn(const ReturnStatement *ret); StmtEvalResult evalTry(const TryStatement *trstmt); StmtEvalResult evalThrow(const ThrowStatement *thrw); const Statement *unwrapLastStatement(const Statement *stmt) { if (stmt->op() == TO_BLOCK) { const Block *b = stmt->asBlock(); auto &stmts = b->statements(); return stmts.empty() ? nullptr : unwrapLastStatement(stmts.back()); } return stmt; } CheckerVisitor *checker; bool canBeString(const Expr *e); public: FunctionReturnTypeEvaluator(CheckerVisitor *c) : checker(c) {} unsigned compute(const Statement *n, bool &allPathsReturn) { StmtEvalResult result = evalStmt(n); allPathsReturn = result.allPathsReturn; unsigned flags = result.flags; if (!allPathsReturn) { flags |= RT_NOTHING; } return flags; } }; } ================================================ FILE: squirrel/compiler/static_analyzer/global_state.cpp ================================================ #include #include "global_state.h" namespace SQCompilation { std::unordered_set knownBindings; std::unordered_set fileNames; std::unordered_map> declaredGlobals; std::unordered_map> usedGlobals; } ================================================ FILE: squirrel/compiler/static_analyzer/global_state.h ================================================ #pragma once #include #include #include #include #include "checker_visitor.h" namespace SQCompilation { extern std::unordered_set knownBindings; extern std::unordered_set fileNames; extern std::unordered_map> declaredGlobals; extern std::unordered_map> usedGlobals; } ================================================ FILE: squirrel/compiler/static_analyzer/loop_terminator_collector.h ================================================ #pragma once namespace SQCompilation { class LoopTerminatorCollector : public Visitor { bool _firstLevel; // means not under some condition, if or switch bool _inSwitch; bool _inTry; void setTerminator(const Statement *t) { if (terminator == nullptr) terminator = t; } public: bool hasCondBreak; bool hasCondContinue; bool hasCondReturn; bool hasCondThrow; bool hasUnconditionalTerm; bool hasUnconditionalContinue; const Statement *terminator; LoopTerminatorCollector(bool firstLevel) : _firstLevel(firstLevel) , _inSwitch(false) , _inTry(false) , hasCondBreak(false) , hasCondContinue(false) , hasCondReturn(false) , hasCondThrow(false) , hasUnconditionalTerm(false) , hasUnconditionalContinue(false) , terminator(nullptr) {} void visitReturnStatement(ReturnStatement *stmt) override { if (_firstLevel) { hasUnconditionalTerm = true; setTerminator(stmt); } else if (!hasUnconditionalTerm) hasCondReturn = true; } void visitThrowStatement(ThrowStatement *stmt) override { if (_firstLevel && !_inTry) { hasUnconditionalTerm = true; setTerminator(stmt); } else if(!hasUnconditionalTerm) hasCondThrow = true; } void visitBreakStatement(BreakStatement *stmt) override { if (_firstLevel) { hasUnconditionalTerm = true; setTerminator(stmt); } else if (!_inSwitch && !hasUnconditionalTerm) hasCondBreak = true; } void visitContinueStatement(ContinueStatement *stmt) override { if (_firstLevel) { hasUnconditionalTerm = true; hasUnconditionalContinue = true; setTerminator(stmt); } else if (!hasUnconditionalTerm) hasCondContinue = true; } void visitIfStatement(IfStatement *stmt) override { bool old = _firstLevel; _firstLevel = false; Visitor::visitIfStatement(stmt); _firstLevel = old; } void visitLoopStatement(LoopStatement *loop) override { // skip - nested loops have their own break/continue/return semantics } void visitFunctionExpr(FunctionExpr *f) override { // skip - function expressions have their own return semantics } void visitDecl(Decl *d) override { // skip } void visitTryStatement(TryStatement *stmt) override { bool old = _inTry; _inTry = true; stmt->tryStatement()->visit(this); _inTry = old; stmt->catchStatement()->visit(this); } void visitSwitchStatement(SwitchStatement *stmt) override { bool oldL = _firstLevel; bool oldS = _inSwitch; _firstLevel = false; _inSwitch = true; Visitor::visitSwitchStatement(stmt); _inSwitch = oldS; _firstLevel = oldL; } bool hasUnconditionalTerminator() const { if (hasCondContinue) return false; return hasUnconditionalTerm; } }; } ================================================ FILE: squirrel/compiler/static_analyzer/modification_checker.h ================================================ #pragma once namespace SQCompilation { class ModificationChecker : public Visitor { bool result = false; public: void visitNode(Node *n) override { TreeOp op = n->op(); switch (op) { case TO_INC: case TO_ASSIGN: case TO_PLUSEQ: case TO_MINUSEQ: case TO_MULEQ: case TO_DIVEQ: case TO_MODEQ: result = true; return; default: n->visitChildren(this); break; } } bool check(Node *n) { result = false; n->visit(this); return result; } }; } ================================================ FILE: squirrel/compiler/static_analyzer/name_shadowing_checker.cpp ================================================ #include "compiler/ast.h" #include "compiler/compilationcontext.h" #include "name_shadowing_checker.h" #include "symbol_info.h" #include "sqtable.h" #include "sqarray.h" #include "sqclass.h" #include "sqfuncproto.h" #include "sqclosure.h" namespace SQCompilation { void NameShadowingChecker::loadBindings(const HSQOBJECT *bindings) { if (bindings && sq_istable(*bindings)) { SQTable *table = _table(*bindings); SQInteger idx = 0; SQObjectPtr pos(idx), key, val; while ((idx = table->Next(false, pos, key, val)) >= 0) { if (sq_isstring(key)) { const char *s = _string(key)->_val; SymbolInfo *info = newSymbolInfo(SK_EXTERNAL_BINDING); declareSymbol(s, info); } pos._unVal.nInteger = idx; } } } const Node *NameShadowingChecker::extractPointedNode(const SymbolInfo *info) { switch (info->kind) { case SK_EXCEPTION: return info->declaration.x; case SK_FUNCTION: return info->declaration.f; case SK_CLASS: return info->declaration.k; case SK_TABLE: return info->declaration.t; case SK_VAR: case SK_BINDING: case SK_FOREACH: return info->declaration.v; case SK_CONST: return info->declaration.c; case SK_ENUM: return info->declaration.e; case SK_ENUM_CONST: return info->declaration.ec->val; case SK_PARAM: return info->declaration.p; case SK_EXTERNAL_BINDING: return &rootPointerNode; default: assert(0); return nullptr; } } void NameShadowingChecker::declareSymbol(const char *name, SymbolInfo *info) { const SymbolInfo *existedInfo = scope->findSymbol(name); if (existedInfo) { bool warn = name[0] != '_' && name[0] != '@'; if (strcmp(name, "this") == 0) { warn = false; } if (existedInfo->ownerScope == info->ownerScope) { // something like `let foo = expr` if ((info->kind == SK_BINDING || info->kind == SK_VAR) && existedInfo->kind == SK_FUNCTION) { const VarDecl *vardecl = info->declaration.v; const FunctionExpr *funcdecl = existedInfo->declaration.f; const Expr *varinit = vardecl->initializer(); if (varinit == funcdecl) { warn = false; } } } if (existedInfo->kind == SK_FUNCTION && info->kind == SK_FUNCTION) { const FunctionExpr *existed = existedInfo->declaration.f; const FunctionExpr *_new = info->declaration.f; if (existed->name()[0] == '(' && _new->name()[0] == '(') { warn = false; } } if (info->kind == SK_PARAM && existedInfo->kind == SK_PARAM && warn) { const ParamDecl *p1 = info->declaration.p; const ParamDecl *p2 = existedInfo->declaration.p; if (p1->isVararg() && p2->isVararg()) { warn = false; } } if (warn) { report(extractPointedNode(info), DiagnosticsId::DI_ID_HIDES_ID, symbolContextName(info->kind), info->name, symbolContextName(existedInfo->kind)); } } scope->symbols[name] = info; } NameShadowingChecker::SymbolInfo *NameShadowingChecker::Scope::findSymbol(const char *name) const { auto it = symbols.find(name); if (it != symbols.end()) { return it->second; } return parent ? parent->findSymbol(name) : nullptr; } void NameShadowingChecker::visitNode(Node *n) { nodeStack.push_back(n); n->visitChildren(this); nodeStack.pop_back(); } void NameShadowingChecker::declareVar(enum SymbolKind k, const VarDecl *var) { const FunctionExpr *f = nullptr; if (k == SK_BINDING) { const Expr *init = var->initializer(); if (init && init->op() == TO_FUNCTION) { k = SK_FUNCTION; f = init->asFunctionExpr(); } } SymbolInfo *info = newSymbolInfo(k); if (f) { info->declaration.f = f; } else { info->declaration.v = var; } info->ownerScope = scope; info->name = var->name(); declareSymbol(var->name(), info); } void NameShadowingChecker::visitVarDecl(VarDecl *var) { Visitor::visitVarDecl(var); declareVar(var->isAssignable() ? SK_VAR : SK_BINDING, var); } void NameShadowingChecker::visitParamDecl(ParamDecl *p) { Visitor::visitParamDecl(p); SymbolInfo *info = newSymbolInfo(SK_PARAM); info->declaration.p = p; info->ownerScope = scope; info->name = p->name(); declareSymbol(p->name(), info); } void NameShadowingChecker::visitConstDecl(ConstDecl *c) { SymbolInfo *info = newSymbolInfo(SK_CONST); info->declaration.c = c; info->ownerScope = scope; info->name = c->name(); declareSymbol(c->name(), info); Visitor::visitConstDecl(c); } void NameShadowingChecker::visitEnumDecl(EnumDecl *e) { SymbolInfo *einfo = newSymbolInfo(SK_ENUM); einfo->declaration.e = e; einfo->ownerScope = scope; einfo->name = e->name(); declareSymbol(e->name(), einfo); for (auto &ec : e->consts()) { SymbolInfo *cinfo = newSymbolInfo(SK_ENUM_CONST); // Are these fully qualified names ever used? const char *fqn = enumFqn(_ctx.arena(), e->name(), ec.id); cinfo->declaration.ec = &ec; cinfo->ownerScope = scope; cinfo->name = fqn; declareSymbol(fqn, cinfo); } } void NameShadowingChecker::visitFunctionExpr(FunctionExpr *f) { Scope *p = scope; Scope funcScope(this, f); bool tableScope = p->owner && (p->owner->op() == TO_CLASS || p->owner->op() == TO_TABLE); if (!tableScope) { SymbolInfo * info = newSymbolInfo(SK_FUNCTION); info->declaration.f = f; info->ownerScope = p; info->name = f->name(); declareSymbol(f->name(), info); } Visitor::visitFunctionExpr(f); } void NameShadowingChecker::visitTableExpr(TableExpr *t) { Scope tableScope(this, t); nodeStack.push_back(t); t->visitChildren(this); nodeStack.pop_back(); } void NameShadowingChecker::visitClassExpr(ClassExpr *k) { Scope klassScope(this, k); nodeStack.push_back(k); k->visitChildren(this); nodeStack.pop_back(); } void NameShadowingChecker::visitBlock(Block *block) { Scope blockScope(this, scope->owner); Visitor::visitBlock(block); } void NameShadowingChecker::visitTryStatement(TryStatement *stmt) { nodeStack.push_back(stmt); stmt->tryStatement()->visit(this); Scope catchScope(this, scope->owner); SymbolInfo *info = newSymbolInfo(SK_EXCEPTION); info->declaration.x = stmt->exceptionId(); info->ownerScope = scope; info->name = stmt->exceptionId()->name(); declareSymbol(stmt->exceptionId()->name(), info); stmt->catchStatement()->visit(this); nodeStack.pop_back(); } void NameShadowingChecker::visitForStatement(ForStatement *stmt) { assert(scope); Scope forScope(this, scope->owner); Visitor::visitForStatement(stmt); } void NameShadowingChecker::visitForeachStatement(ForeachStatement *stmt) { assert(scope); Scope foreachScope(this, scope->owner); VarDecl *idx = stmt->idx(); if (idx) { declareVar(SK_FOREACH, idx); assert(idx->initializer() == nullptr); } VarDecl *val = stmt->val(); if (val) { declareVar(SK_FOREACH, val); assert(val->initializer() == nullptr); } nodeStack.push_back(stmt); stmt->container()->visit(this); stmt->body()->visit(this); nodeStack.pop_back(); } } ================================================ FILE: squirrel/compiler/static_analyzer/name_shadowing_checker.h ================================================ #pragma once #include "analyzer_internal.h" #include "symbol_info.h" namespace SQCompilation { class NameShadowingChecker : public Visitor { SQCompilationContext _ctx; std::vector nodeStack; void report(const Node *n, int32_t id, ...) { va_list vargs; va_start(vargs, id); _ctx.vreportDiagnostic((enum DiagnosticsId)id, n->lineStart(), n->columnStart(), n->textWidth(), vargs); //-V522 va_end(vargs); } struct Scope; struct SymbolInfo { union { const Id *x; const FunctionExpr *f; const ClassExpr *k; const VarDecl *v; const TableExpr *t; const ParamDecl *p; const EnumDecl *e; const ConstDecl *c; const EnumConst *ec; } declaration; SymbolKind kind; const struct Scope *ownerScope; const char *name; SymbolInfo(SymbolKind k) : kind(k), declaration(), name(nullptr), ownerScope(nullptr) {} }; struct Scope { NameShadowingChecker *checker; Scope(NameShadowingChecker *chk, const Node *o) : checker(chk), owner(o), symbols() { parent = checker->scope; checker->scope = this; } ~Scope() { checker->scope = parent; } struct Scope *parent; std::unordered_map symbols; const Node *owner; SymbolInfo *findSymbol(const char *name) const; }; const Node *extractPointedNode(const SymbolInfo *info); SymbolInfo *newSymbolInfo(SymbolKind k) { void *mem = _ctx.arena()->allocate(sizeof(SymbolInfo)); return new(mem) SymbolInfo(k); } struct Scope rootScope; struct Scope *scope; void loadBindings(const HSQOBJECT *bindings); void declareVar(SymbolKind k, const VarDecl *v); void declareSymbol(const char *name, SymbolInfo *info); Id rootPointerNode; public: NameShadowingChecker(SQCompilationContext &ctx, const HSQOBJECT *bindings) : _ctx(ctx) , rootScope(this, nullptr) , scope(&rootScope) , rootPointerNode(SourceSpan::invalid(), "") { rootScope.parent = nullptr; loadBindings(bindings); } void visitNode(Node *n); void visitVarDecl(VarDecl *var); void visitFunctionExpr(FunctionExpr *f); void visitParamDecl(ParamDecl *p); void visitConstDecl(ConstDecl *c); void visitEnumDecl(EnumDecl *e); void visitClassExpr(ClassExpr *k); void visitTableExpr(TableExpr *t); void visitBlock(Block *block); void visitTryStatement(TryStatement *stmt); void visitForStatement(ForStatement *stmt); void visitForeachStatement(ForeachStatement *stmt); void analyze(RootBlock *root) { root->visit(this); } }; } ================================================ FILE: squirrel/compiler/static_analyzer/naming.cpp ================================================ #include #include "naming.h" #include "ast_helpers.h" namespace SQCompilation { bool looksLikeElementCount(const Expr *e) { const char *checkee = nullptr; if (e->op() == TO_ID) checkee = e->asId()->name(); else if (e->op() == TO_GETFIELD) { checkee = e->asGetField()->fieldName(); } else if (e->op() == TO_GETSLOT) { return looksLikeElementCount(deparenStatic(e->asGetSlot()->key())); } else if (e->op() == TO_CALL) { return looksLikeElementCount(deparenStatic(e->asCallExpr()->callee())); } if (!checkee) return false; static const char * markers[] = { "len", "Len", "length", "Length", "count", "Count", "cnt", "Cnt", "size", "Size", "Num", "Number" }; for (const char *m : markers) { const char *p = strstr(checkee, m); if (!p) continue; if (p == checkee || p[-1] == '_' || (sq_islower(p[-1]) && sq_isupper(p[0]))) { char next = p[strlen(m)]; if (!next || next == '_' || sq_isupper(next)) return true; } } return false; } bool isUpperCaseIdentifier(const Expr *e) { const char *id = nullptr; if (e->op() == TO_GETFIELD) { id = e->asGetField()->fieldName(); } if (e->op() == TO_ID) { id = e->asId()->name(); } else if (e->op() == TO_LITERAL && e->asLiteral()->kind() == LK_STRING) { id = e->asLiteral()->s(); } if (!id) return false; while (*id) { if (*id >= 'a' && *id <= 'z') return false; ++id; } return true; } } ================================================ FILE: squirrel/compiler/static_analyzer/naming.h ================================================ #pragma once #include #include #include "compiler/compilationcontext.h" #include "compiler/ast.h" #include "config.h" #include namespace SQCompilation { inline const char *terminatorOpToName(TreeOp op) { switch (op) { case TO_BREAK: return "break"; case TO_CONTINUE: return "continue"; case TO_RETURN: return "return"; case TO_THROW: return "throw"; default: assert(0); return ""; } } inline bool isValidId(const char *id) { assert(id != nullptr); if (!sq_isalpha(id[0]) && id[0] != '_') return false; for (int i = 1; id[i]; ++i) { char c = id[i]; if (!sq_isalpha(c) && !sq_isdigit(c) && c != '_') return false; } return true; } inline bool hasAnyPrefix(const char *str, const std::vector &prefixes) { for (auto &prefix : prefixes) { size_t length = prefix.length(); bool hasPrefix = strncmp(str, prefix.c_str(), length)==0; if (hasPrefix) { char c = str[length]; if (!c || c == '_' || c != tolower(c)) { return true; } } } return false; } inline bool hasAnyEqual(const char *str, const std::vector &candidates) { for (auto &candidate : candidates) { if (strcmp(str, candidate.c_str()) == 0) { return true; } } return false; } inline bool hasAnySubstring(const char *str, const std::vector &candidates) { for (auto &candidate : candidates) { if (strstr(str, candidate.c_str())) { return true; } } return false; } inline bool nameLooksLikeResultMustBeBoolean(const char *funcName) { if (!funcName) return false; return hasAnyPrefix(funcName, function_should_return_bool_prefix); } inline bool nameLooksLikeFunctionMustReturnResult(const char *funcName) { if (!funcName) return false; bool nameInList = nameLooksLikeResultMustBeBoolean(funcName) || hasAnyPrefix(funcName, function_should_return_something_prefix); if (!nameInList) if ((strstr(funcName, "_ctor") || strstr(funcName, "Ctor")) && strstr(funcName, "set") != funcName) nameInList = true; return nameInList; } inline bool nameLooksLikeResultMustBeUtilized(const char *name) { return hasAnyEqual(name, function_result_must_be_utilized); } inline bool nameLooksLikeResultMustBeString(const char *name) { return hasAnyEqual(name, function_can_return_string); } inline bool canFunctionReturnNull(const char *n) { return hasAnyEqual(n, function_can_return_null); } inline bool isForbiddenFunctionName(const char *n) { return hasAnyEqual(n, function_forbidden); } inline bool nameLooksLikeMustBeCalledFromRoot(const char *n) { return hasAnyEqual(n, function_must_be_called_from_root); } inline bool nameLooksLikeForbiddenParentDir(const char *n) { return hasAnyEqual(n, function_forbidden_parent_dir); } inline bool nameLooksLikeFormatFunction(const char *n) { std::string transformed(n); std::transform(transformed.begin(), transformed.end(), transformed.begin(), ::tolower); return hasAnySubstring(transformed.c_str(), format_function_name); } inline bool nameLooksLikeModifiesObject(const char *n) { return hasAnyEqual(n, function_modifies_object); } inline bool nameLooksLikeFunctionTakeBooleanLambda(const char *n) { return hasAnyEqual(n, function_takes_boolean_lambda); } inline bool nameLooksLikeRequiresResultFromCallback(const char *n) { return hasAnyEqual(n, function_requires_result_from_callback); } inline bool nameLooksLikeIgnoresCallbackResult(const char *n) { return hasAnyEqual(n, function_ignores_callback_result); } bool looksLikeElementCount(const Expr *e); bool isUpperCaseIdentifier(const Expr *e); } ================================================ FILE: squirrel/compiler/static_analyzer/node_complexity_counter.h ================================================ #pragma once namespace SQCompilation { class NodeComplexityComputer : public Visitor { int32_t complexity; const int32_t limit; NodeComplexityComputer(int32_t l) : limit(l), complexity(0) {} public: void visitNode(Node *n) override { if (complexity > limit) return; Visitor::visitNode(n); } // Expressions void visitId(Id *id) { complexity += 1; } void visitLiteralExpr(LiteralExpr *l) { } void visitUnExpr(UnExpr *u) { if (u->op() != TO_PAREN) complexity += 1; Visitor::visitUnExpr(u); } void visitBinExpr(BinExpr *b) { complexity += 3; Visitor::visitBinExpr(b); } void visitTerExpr(TerExpr *t) { complexity += 2; Visitor::visitTerExpr(t); } void visitGetFieldExpr(GetFieldExpr *f) { complexity += 1; Visitor::visitGetFieldExpr(f); } void visitGetSlotExpr(GetSlotExpr *t) { complexity += 2; Visitor::visitGetSlotExpr(t); } void visitCallExpr(CallExpr *call) { complexity += (call->arguments().size() - 1); Visitor::visitCallExpr(call); } void visitIncExpr(IncExpr *inc) { complexity += 1; Visitor::visitIncExpr(inc); } void visitArrayExpr(ArrayExpr *arr) { complexity += arr->initializers().size(); Visitor::visitArrayExpr(arr); } void visitCommaExpr(CommaExpr *comma) { complexity += comma->expressions().size(); Visitor::visitCommaExpr(comma); } // Statements void visitBlock(Block *b) { complexity += b->statements().size(); Visitor::visitBlock(b); } void visitIfStatement(IfStatement *ifstmt) { complexity += 2; if (ifstmt->elseBranch()) complexity += 1; Visitor::visitIfStatement(ifstmt); } void visitSwitchStatement(SwitchStatement *swtch) { complexity += 1; // switch expr complexity += (swtch->cases().size() * 2); // 2 due to label + body; if (swtch->defaultCase().stmt) complexity += 1; Visitor::visitSwitchStatement(swtch); } void visitTryStatement(TryStatement *trystmt) { complexity += 3; // try + ex + catch Visitor::visitTryStatement(trystmt); } void visitTerminateStatement(TerminateStatement *t) { if (t->argument()) complexity += 1; Visitor::visitTerminateStatement(t); } void visitLoopStatement(LoopStatement *l) { complexity += 1; Visitor::visitLoopStatement(l); } void visitWhileStatement(WhileStatement *w) { complexity += 1; Visitor::visitWhileStatement(w); } void visitDoWhileStatement(DoWhileStatement *dw) { complexity += 1; Visitor::visitDoWhileStatement(dw); } void visitForStatement(ForStatement *f) { if (f->initializer()) complexity += 1; if (f->condition()) complexity += 1; if (f->modifier()) complexity += 1; Visitor::visitForStatement(f); } void visitForeachStatement(ForeachStatement *fe) { if (fe->idx()) complexity += 1; if (fe->val()) complexity += 1; complexity += 1; Visitor::visitForeachStatement(fe); } // Declarations void visitValueDecl(ValueDecl *v) { complexity += 1; if (v->expression()) complexity += 1; Visitor::visitValueDecl(v); } void visitTableExpr(TableExpr *t) { complexity += (t->members().size() * 2); // key + value Visitor::visitTableExpr(t); } void visitClassExpr(ClassExpr *c) { if (c->classBase()) complexity += 1; if (c->classKey()) complexity += 1; Visitor::visitClassExpr(c); } void visitFunctionExpr(FunctionExpr *f) { complexity += f->parameters().size(); Visitor::visitFunctionExpr(f); } void visitEnumDecl(EnumDecl *e) { complexity += 1; //name complexity += (e->consts().size() * 2); Visitor::visitEnumDecl(e); } void visitConstDecl(ConstDecl *c) { complexity += 2; Visitor::visitConstDecl(c); } void visitDeclGroup(DeclGroup *g) { complexity += g->declarations().size(); Visitor::visitDeclGroup(g); } void visitDestructuringDecl(DestructuringDecl *dd) { complexity += 1; Visitor::visitDestructuringDecl(dd); } static int32_t compute(const Node *n, int32_t limit) { NodeComplexityComputer c(limit); const_cast(n)->visit(&c); return c.complexity; } }; } ================================================ FILE: squirrel/compiler/static_analyzer/node_diff_computer.h ================================================ #pragma once namespace SQCompilation { class NodeDiffComputer { const struct { const int32_t OpDiffCost = 40; const int32_t SizeDiffCost = 30; const int32_t SizeDiffCoeff = 11; const int32_t NullDiffCost = SizeDiffCost; const int32_t IncFormDiffCost = 2; const int32_t IncValDiffCost = 1; const int32_t NameDiffCost = 1; const int32_t LiteralDiffCost = 1; const int32_t MutabilityDiffCost = 1; const int32_t StaticMemberDiffCost = 2; const int32_t NullabilityDiffCost = 3; } DiffCosts; const int32_t limit; int32_t sizeDiff(const int32_t l, const int32_t r) const { return DiffCosts.SizeDiffCost + std::abs(l - r) * DiffCosts.SizeDiffCoeff; } NodeDiffComputer(int32_t l) : limit(l) {} int32_t average(int32_t total, int32_t size) const { int32_t aver = size ? total / size : 0; if (aver == 0) aver = 1; return aver; } int32_t diffBlock(const Block *lhs, const Block *rhs) { const auto &l = lhs->statements(); const auto &r = rhs->statements(); if (l.size() != r.size()) return sizeDiff(l.size(), r.size()); int32_t total = 0; for (int32_t i = 0; i < l.size(); ++i) { int32_t tmp = diffNodes(l[i], r[i]); if (tmp > limit) return tmp; total += tmp; } return total; } int32_t diffIf(const IfStatement *lhs, const IfStatement *rhs) { int32_t condDiff = diffNodes(lhs->condition(), rhs->condition()); if (condDiff > limit) return condDiff; int32_t thenDiff = diffNodes(lhs->thenBranch(), rhs->thenBranch()); if (thenDiff > limit) return thenDiff; int32_t elseDiff = diffNodes(lhs->elseBranch(), rhs->elseBranch()); if (elseDiff > limit) return elseDiff; return condDiff + thenDiff + elseDiff; } int32_t diffWhile(const WhileStatement *lhs, const WhileStatement *rhs) { int32_t condDiff = diffNodes(lhs->condition(), rhs->condition()); if (condDiff > limit) return condDiff; int32_t bodyDiff = diffNodes(lhs->body(), rhs->body()); if (bodyDiff > limit) return bodyDiff; return condDiff + bodyDiff; } int32_t diffDoWhile(const DoWhileStatement *lhs, const DoWhileStatement *rhs) { int32_t bodyDiff = diffNodes(lhs->body(), rhs->body()); if (bodyDiff > limit) return bodyDiff; int32_t condDiff = diffNodes(lhs->condition(), rhs->condition()); if (condDiff > limit) return condDiff; return bodyDiff + condDiff; } int32_t diffFor(const ForStatement *lhs, const ForStatement *rhs) { int32_t initDiff = diffNodes(lhs->initializer(), rhs->initializer()); if (initDiff > limit) return initDiff; int32_t condDiff = diffNodes(lhs->condition(), rhs->condition()); if (condDiff > limit) return condDiff; int32_t modDiff = diffNodes(lhs->modifier(), rhs->modifier()); if (modDiff > limit) return modDiff; int32_t bodyDiff = diffNodes(lhs->body(), rhs->body()); if (bodyDiff > limit) return bodyDiff; return initDiff + condDiff + modDiff + bodyDiff; } int32_t diffForeach(const ForeachStatement *lhs, const ForeachStatement *rhs) { int32_t idxDiff = diffNodes(lhs->idx(), rhs->idx()); if (idxDiff > limit) return idxDiff; int32_t valDiff = diffNodes(lhs->val(), rhs->val()); if (valDiff > limit) return valDiff; int32_t contDiff = diffNodes(lhs->container(), rhs->container()); if (contDiff > limit) return contDiff; int32_t bodyDiff = diffNodes(lhs->body(), rhs->body()); if (bodyDiff > limit) return bodyDiff; return idxDiff + valDiff + contDiff + bodyDiff; } int32_t diffSwitch(const SwitchStatement *lhs, const SwitchStatement *rhs) { int32_t exprDiff = diffNodes(lhs->expression(), rhs->expression()); if (exprDiff > limit) return exprDiff; const auto &casesLeft = lhs->cases(); const auto &casesRight = rhs->cases(); if (casesLeft.size() != casesRight.size()) return sizeDiff(casesLeft.size(), casesRight.size()); bool leftHasDefault = lhs->defaultCase().stmt != nullptr; bool rightHasDefault = rhs->defaultCase().stmt != nullptr; if (leftHasDefault != rightHasDefault) // shortcut return DiffCosts.NullDiffCost; int32_t casesDiff = 0; for (int32_t i = 0; i < int32_t(casesLeft.size()); ++i) { const auto &l = casesLeft[i]; const auto &r = casesRight[i]; int32_t valDiff = diffNodes(l.val, r.val); if (valDiff > limit) return valDiff; int32_t bodyDiff = diffNodes(l.stmt, r.stmt); if (bodyDiff > limit) return bodyDiff; casesDiff += (valDiff + bodyDiff); } int32_t defaultDiff = diffNodes(lhs->defaultCase().stmt, rhs->defaultCase().stmt); if (defaultDiff > limit) return defaultDiff; return casesDiff + defaultDiff + exprDiff; } int32_t diffTerminator(const TerminateStatement *lhs, const TerminateStatement *rhs) { assert(lhs->op() == rhs->op()); return diffNodes(lhs->argument(), rhs->argument()); } int32_t diffTry(const TryStatement *lhs, const TryStatement *rhs) { int32_t tryDiff = diffNodes(lhs->tryStatement(), rhs->tryStatement()); if (tryDiff > limit) return tryDiff; int32_t exIdDiff = diffNodes(lhs->exceptionId(), rhs->exceptionId()); int32_t catchDiff = diffNodes(lhs->catchStatement(), rhs->catchStatement()); if (catchDiff > limit) return catchDiff; return tryDiff + exIdDiff + catchDiff; } int32_t diffExprStmt(const ExprStatement *lhs, const ExprStatement *rhs) { return diffNodes(lhs->expression(), rhs->expression()); } int32_t diffId(const Id *lhs, const Id *rhs) { return strcmp(lhs->name(), rhs->name()) != 0 ? DiffCosts.NameDiffCost : 0; } int32_t diffComma(const CommaExpr *lhs, const CommaExpr *rhs) { const auto &leftExpressions = lhs->expressions(); const auto &rightExpression = rhs->expressions(); if (leftExpressions.size() != rightExpression.size()) return sizeDiff(leftExpressions.size(), rightExpression.size()); int32_t result = 0; for (int32_t i = 0; i < leftExpressions.size(); ++i) { const Expr *l = leftExpressions[i]; const Expr *r = rightExpression[i]; int32_t tmp = diffNodes(l, r); if (tmp > limit) return tmp; result += tmp; } return result; } int32_t diffBinary(const BinExpr *lhs, const BinExpr *rhs) { assert(lhs->op() == rhs->op()); int32_t diffLeft = diffNodes(lhs->lhs(), rhs->lhs()); if (diffLeft > limit) return diffLeft; int32_t diffRight = diffNodes(lhs->rhs(), rhs->rhs()); if (diffRight > limit) return diffRight; return diffLeft + diffRight; } int32_t diffUnary(const UnExpr *lhs, const UnExpr *rhs) { assert(lhs->op() == rhs->op()); return diffNodes(lhs->argument(), rhs->argument()); } int32_t diffLiterals(const LiteralExpr *lhs, const LiteralExpr *rhs) { if (lhs->kind() != rhs->kind()) return DiffCosts.LiteralDiffCost; switch (lhs->kind()) { case LK_NULL: return 0; case LK_BOOL: return lhs->b() != rhs->b() ? DiffCosts.LiteralDiffCost : 0; case LK_INT: return lhs->i() != rhs->i() ? DiffCosts.LiteralDiffCost : 0; case LK_FLOAT: return lhs->f() != rhs->f() ? DiffCosts.LiteralDiffCost : 0; case LK_STRING: return strcmp(lhs->s(), rhs->s()) != 0 ? DiffCosts.LiteralDiffCost : 0; default: assert(0); return -1000; } } int32_t diffIncExpr(const IncExpr *lhs, const IncExpr *rhs) { int32_t result = 0; if (lhs->form() != rhs->form()) result += DiffCosts.IncFormDiffCost; if (lhs->diff() != rhs->diff()) result += DiffCosts.IncValDiffCost; int32_t argDiff = diffNodes(lhs->argument(), rhs->argument()); if (argDiff > limit) return argDiff; return result + argDiff; } int32_t diffArrayExpr(const ArrayExpr *lhs, const ArrayExpr *rhs) { const auto &leftInits = lhs->initializers(); const auto &rightInits = rhs->initializers(); if (leftInits.size() != rightInits.size()) return sizeDiff(leftInits.size(), rightInits.size()); int32_t result = 0; for (int32_t i = 0; i < leftInits.size(); ++i) { const Expr *l = leftInits[i]; const Expr *r = rightInits[i]; int32_t tmp = diffNodes(l, r); if (tmp > limit) return tmp; result += tmp; } return result; } int32_t diffGetField(const GetFieldExpr *lhs, const GetFieldExpr *rhs) { int32_t receiverDiff = diffNodes(lhs->receiver(), rhs->receiver()); if (receiverDiff > limit) return receiverDiff; if (strcmp(lhs->fieldName(), rhs->fieldName()) != 0) receiverDiff += DiffCosts.NameDiffCost; if (lhs->isNullable() != rhs->isNullable()) receiverDiff += DiffCosts.NullabilityDiffCost; return receiverDiff; } int32_t diffGetSlot(const GetSlotExpr *lhs, const GetSlotExpr *rhs) { int32_t receiverDiff = diffNodes(lhs->receiver(), rhs->receiver()); if (receiverDiff > limit) return receiverDiff; int32_t keyDiff = diffNodes(lhs->key(), rhs->key()); if (keyDiff > limit) return keyDiff; if (lhs->isNullable() != rhs->isNullable()) receiverDiff += DiffCosts.NullabilityDiffCost; return receiverDiff + keyDiff; } int32_t diffCallExpr(const CallExpr *lhs, const CallExpr *rhs) { auto &leftArgs = lhs->arguments(); auto &rightArgs = rhs->arguments(); if (leftArgs.size() != rightArgs.size()) return sizeDiff(leftArgs.size(), rightArgs.size()); int32_t calleeDiff = diffNodes(lhs->callee(), rhs->callee()); if (calleeDiff > limit) return calleeDiff; int32_t argsDiff = 0; for (int32_t i = 0; i < leftArgs.size(); ++i) { const Expr *l = leftArgs[i]; const Expr *r = rightArgs[i]; int32_t tmp = diffNodes(l, r); if (tmp > limit) return tmp; argsDiff += tmp; } if (lhs->isNullable() != rhs->isNullable()) calleeDiff += DiffCosts.NullabilityDiffCost; return argsDiff + calleeDiff; } int32_t diffTernary(const TerExpr *lhs, const TerExpr *rhs) { int32_t condDiff = diffNodes(lhs->a(), rhs->a()); if (condDiff > limit) return condDiff; int32_t tDiff = diffNodes(lhs->b(), rhs->b()); if (tDiff > limit) return tDiff; int32_t eDiff = diffNodes(lhs->c(), rhs->c()); if (eDiff > limit) return eDiff; return condDiff + tDiff + eDiff; } int32_t diffValueDecl(const ValueDecl *lhs, const ValueDecl *rhs) { int32_t result = strcmp(lhs->name(), rhs->name()) != 0 ? DiffCosts.NameDiffCost : 0; result += diffNodes(lhs->expression(), rhs->expression()); return result; } int32_t diffConst(const ConstDecl *lhs, const ConstDecl *rhs) { int32_t nameDiff = strcmp(lhs->name(), rhs->name()) != 0 ? DiffCosts.NameDiffCost : 0; int32_t valueDiff = diffNodes(lhs->value(), rhs->value()); return nameDiff + valueDiff; } int32_t diffVarDecl(const VarDecl *lhs, const VarDecl *rhs) { int32_t diffV = diffValueDecl(lhs, rhs); if (lhs->isAssignable() != rhs->isAssignable()) diffV += DiffCosts.MutabilityDiffCost; return diffV; } int32_t diffDeclGroup(const DeclGroup *lhs, const DeclGroup *rhs) { const auto &leftGroup = lhs->declarations(); const auto &rightGroup = rhs->declarations(); if (leftGroup.size() != rightGroup.size()) return sizeDiff(leftGroup.size(), rightGroup.size()); int32_t result = 0; for (int32_t i = 0; i < leftGroup.size(); ++i) { const VarDecl *l = leftGroup[i]; const VarDecl *r = rightGroup[i]; int32_t tmp = diffVarDecl(l, r); if (tmp > limit) return tmp; result += tmp; } return result; } int32_t diffDestructDecl(const DestructuringDecl *lhs, const DestructuringDecl *rhs) { int32_t valueDiff = diffNodes(lhs->initExpression(), rhs->initExpression()); if (valueDiff > limit) return valueDiff; int32_t groupDiff = diffDeclGroup(lhs, rhs); if (groupDiff > limit) return groupDiff; return valueDiff + groupDiff; } const char *realFunctionName(const FunctionExpr *f) const { const char *name = f->name(); assert(name); if (name[0] == '(') // anonymous return ""; return name; } int32_t diffDirective(const DirectiveStmt *lhs, const DirectiveStmt *rhs) { if (lhs->setFlags == rhs->setFlags && lhs->clearFlags == rhs->clearFlags && lhs->applyToDefault == rhs->applyToDefault) return 0; else return DiffCosts.OpDiffCost; } int32_t diffFunction(const FunctionExpr *lhs, const FunctionExpr *rhs) { int32_t nameDiff = strcmp(realFunctionName(lhs), realFunctionName(rhs)) != 0 ? DiffCosts.NameDiffCost : 0; const auto &leftParams = lhs->parameters(); const auto &rightParams = rhs->parameters(); if (leftParams.size() != rightParams.size()) return sizeDiff(leftParams.size(), rightParams.size()); int32_t paramDiff = 0; for (int32_t i = 0; i < leftParams.size(); ++i) { const ParamDecl *l = leftParams[i]; const ParamDecl *r = rightParams[i]; int32_t tmp = diffValueDecl(l, r); if (tmp > limit) return tmp; paramDiff += tmp; } int32_t bodyDiff = diffNodes(lhs->body(), rhs->body()); if (bodyDiff > limit) return bodyDiff; return nameDiff + paramDiff + bodyDiff; } int32_t diffTable(const TableExpr *lhs, const TableExpr *rhs) { const auto &leftMembers = lhs->members(); const auto &rightMembers = rhs->members(); if (leftMembers.size() != rightMembers.size()) return sizeDiff(leftMembers.size(), rightMembers.size()); int32_t tableDiff = 0; for (int32_t i = 0; i < leftMembers.size(); ++i) { const auto &l = leftMembers[i]; const auto &r = rightMembers[i]; int32_t keyDiff = diffNodes(l.key, r.key); if (keyDiff > limit) return keyDiff; int32_t valueDiff = diffNodes(l.value, r.value); if (valueDiff > limit) return valueDiff; tableDiff += (keyDiff + valueDiff); if (l.isStatic() != r.isStatic()) tableDiff += DiffCosts.StaticMemberDiffCost; } return tableDiff; } int32_t diffClass(const ClassExpr *lhs, const ClassExpr *rhs) { int32_t keyDiff = diffNodes(lhs->classKey(), rhs->classKey()); if (keyDiff > limit) return keyDiff; int32_t baseDiff = diffNodes(lhs->classBase(), rhs->classBase()); if (baseDiff > limit) return baseDiff; int32_t bodyDiff = diffTable(lhs, rhs); if (bodyDiff > limit) return bodyDiff; return keyDiff + baseDiff + bodyDiff; } int32_t diffEnumDecl(const EnumDecl *lhs, const EnumDecl *rhs) { int32_t nameDiff = strcmp(lhs->name(), rhs->name()) != 0 ? DiffCosts.NameDiffCost : 0; const auto &leftValues = lhs->consts(); const auto &rightValues = rhs->consts(); if (leftValues.size() != rightValues.size()) return sizeDiff(leftValues.size(), rightValues.size()); int32_t valueDiff = 0; for (int32_t i = 0; i < leftValues.size(); ++i) { const auto &l = leftValues[i]; const auto &r = rightValues[i]; int32_t cNameDiff = strcmp(l.id, r.id) != 0 ? DiffCosts.NameDiffCost : 0; int32_t cValueDiff = diffLiterals(l.val, r.val); valueDiff += (cNameDiff + cValueDiff); } return nameDiff + valueDiff; } int32_t diffNodes(const Node *lhs, const Node *rhs) { if (lhs == rhs) return 0; if (!lhs || !rhs) { return DiffCosts.NullDiffCost; } if (lhs->op() != rhs->op()) { return DiffCosts.OpDiffCost; } switch (lhs->op()) { case TO_BLOCK: return diffBlock((const Block *)lhs, (const Block *)rhs); case TO_IF: return diffIf((const IfStatement *)lhs, (const IfStatement *)rhs); case TO_WHILE: return diffWhile((const WhileStatement *)lhs, (const WhileStatement *)rhs); case TO_DOWHILE: return diffDoWhile((const DoWhileStatement *)lhs, (const DoWhileStatement *)rhs); case TO_FOR: return diffFor((const ForStatement *)lhs, (const ForStatement *)rhs); case TO_FOREACH: return diffForeach((const ForeachStatement *)lhs, (const ForeachStatement *)rhs); case TO_SWITCH: return diffSwitch((const SwitchStatement *)lhs, (const SwitchStatement *)rhs); case TO_RETURN: case TO_YIELD: case TO_THROW: return diffTerminator((const TerminateStatement *)lhs, (const TerminateStatement *)rhs); case TO_TRY: return diffTry((const TryStatement *)lhs, (const TryStatement *)rhs); case TO_BREAK: case TO_CONTINUE: case TO_EMPTY: case TO_BASE: case TO_ROOT_TABLE_ACCESS: return 0; case TO_DIRECTIVE: return diffDirective((const DirectiveStmt*)lhs, (const DirectiveStmt*)rhs); case TO_EXPR_STMT: return diffExprStmt((const ExprStatement *)lhs, (const ExprStatement *)rhs); //case TO_STATEMENT_MARK: case TO_ID: return diffId((const Id *)lhs, (const Id *)rhs); case TO_COMMA: return diffComma((const CommaExpr *)lhs, (const CommaExpr *)rhs); case TO_NULLC: case TO_ASSIGN: case TO_OROR: case TO_ANDAND: case TO_OR: case TO_XOR: case TO_AND: case TO_NE: case TO_EQ: case TO_3CMP: case TO_GE: case TO_GT: case TO_LE: case TO_LT: case TO_IN: case TO_INSTANCEOF: case TO_USHR: case TO_SHR: case TO_SHL: case TO_MUL: case TO_DIV: case TO_MOD: case TO_ADD: case TO_SUB: case TO_NEWSLOT: case TO_PLUSEQ: case TO_MINUSEQ: case TO_MULEQ: case TO_DIVEQ: case TO_MODEQ: return diffBinary((const BinExpr *)lhs, (const BinExpr *)rhs); case TO_NOT: case TO_BNOT: case TO_NEG: case TO_TYPEOF: case TO_STATIC_MEMO: case TO_INLINE_CONST: case TO_RESUME: case TO_CLONE: case TO_PAREN: case TO_DELETE: return diffUnary((const UnExpr *)lhs, (const UnExpr *)rhs); case TO_LITERAL: return diffLiterals((const LiteralExpr *)lhs, (const LiteralExpr *)rhs); case TO_INC: return diffIncExpr((const IncExpr *)lhs, (const IncExpr *)rhs); case TO_TABLE: return diffTable((const TableExpr *)lhs, (const TableExpr *)rhs); case TO_CLASS: return diffClass((const ClassExpr *)lhs, (const ClassExpr *)rhs); case TO_FUNCTION: return diffFunction((const FunctionExpr *)lhs, (const FunctionExpr *)rhs); case TO_ARRAY: return diffArrayExpr((const ArrayExpr *)lhs, (const ArrayExpr *)rhs); case TO_GETFIELD: return diffGetField((const GetFieldExpr *)lhs, (const GetFieldExpr *)rhs); case TO_GETSLOT: return diffGetSlot((const GetSlotExpr *)lhs, (const GetSlotExpr *)rhs); case TO_CALL: return diffCallExpr((const CallExpr *)lhs, (const CallExpr *)rhs); case TO_TERNARY: return diffTernary((const TerExpr *)lhs, (const TerExpr *)rhs); //case TO_EXPR_MARK: case TO_VAR: return diffVarDecl((const VarDecl *)lhs, (const VarDecl *)rhs); case TO_PARAM: return diffValueDecl((const ValueDecl *)lhs, (const ValueDecl *)rhs); case TO_CONST: return diffConst((const ConstDecl *)lhs, (const ConstDecl *)rhs); case TO_DECL_GROUP: return diffDeclGroup((const DeclGroup *)lhs, (const DeclGroup *)rhs); case TO_DESTRUCTURE: return diffDestructDecl((const DestructuringDecl *)lhs, (const DestructuringDecl *)rhs); case TO_ENUM: return diffEnumDecl((const EnumDecl *)lhs, (const EnumDecl *)rhs); case TO_SETFIELD: case TO_SETSLOT: default: assert(0); return -1; } } public: static int32_t compute(const Node *lhs, const Node *rhs, int32_t limit) { NodeDiffComputer c(limit); return c.diffNodes(lhs, rhs); } }; } ================================================ FILE: squirrel/compiler/static_analyzer/node_equal_checker.h ================================================ #pragma once #include "compiler/ast.h" namespace SQCompilation { class NodeEqualChecker { template bool cmpNodeVector(const ArenaVector &lhs, const ArenaVector &rhs) const { if (lhs.size() != rhs.size()) return false; for (int32_t i = 0; i < lhs.size(); ++i) { if (!check(lhs[i], rhs[i])) return false; } return true; } bool cmpId(const Id *l, const Id* r) const { return strcmp(l->name(), r->name()) == 0; } bool cmpLiterals(const LiteralExpr *l, const LiteralExpr *r) const { if (l->kind() != r->kind()) return false; switch (l->kind()) { case LK_STRING: return strcmp(l->s(), r->s()) == 0; default: return l->raw() == r->raw(); } } bool cmpBinary(const BinExpr *l, const BinExpr *r) const { return check(l->lhs(), r->lhs()) && check(l->rhs(), r->rhs()); } bool cmpUnary(const UnExpr *l, const UnExpr *r) const { return check(l->argument(), r->argument()); } bool cmpTernary(const TerExpr *l, const TerExpr *r) const { return check(l->a(), r->a()) && check(l->b(), r->b()) && check(l->c(), r->c()); } bool cmpBlock(const Block *l, const Block *r) const { if (l->isRoot() != r->isRoot()) return false; if (l->isBody() != r->isBody()) return false; return cmpNodeVector(l->statements(), r->statements()); } bool cmpIf(const IfStatement *l, const IfStatement *r) const { if (!check(l->condition(), r->condition())) return false; if (!check(l->thenBranch(), r->thenBranch())) return false; return check(l->elseBranch(), r->elseBranch()); } bool cmpWhile(const WhileStatement *l, const WhileStatement *r) const { if (!check(l->condition(), r->condition())) return false; return check(l->body(), r->body()); } bool cmpDoWhile(const DoWhileStatement *l, const DoWhileStatement *r) const { if (!check(l->body(), r->body())) return false; return check(l->condition(), r->condition()); } bool cmpFor(const ForStatement *l, const ForStatement *r) const { if (!check(l->initializer(), r->initializer())) return false; if (!check(l->condition(), r->condition())) return false; if (!check(l->modifier(), r->modifier())) return false; return check(l->body(), r->body()); } bool cmpForeach(const ForeachStatement *l, const ForeachStatement *r) const { if (!check(l->idx(), r->idx())) return false; if (!check(l->val(), r->val())) return false; if (!check(l->container(), r->container())) return false; return check(l->body(), r->body()); } bool cmpSwitch(const SwitchStatement *l, const SwitchStatement *r) const { if (!check(l->expression(), r->expression())) return false; const auto &lcases = l->cases(); const auto &rcases = r->cases(); if (lcases.size() != rcases.size()) return false; for (int32_t i = 0; i < lcases.size(); ++i) { const auto &lc = lcases[i]; const auto &rc = rcases[i]; if (!check(lc.val, rc.val)) return false; if (!check(lc.stmt, rc.stmt)) return false; } return check(l->defaultCase().stmt, r->defaultCase().stmt); } bool cmpTry(const TryStatement *l, const TryStatement *r) const { if (!check(l->tryStatement(), r->tryStatement())) return false; if (!check(l->exceptionId(), r->exceptionId())) return false; return check(l->catchStatement(), r->catchStatement()); } bool cmpTerminate(const TerminateStatement *l, const TerminateStatement *r) const { return check(l->argument(), r->argument()); } bool cmpReturn(const ReturnStatement *l, const ReturnStatement *r) const { return l->isLambdaReturn() == r->isLambdaReturn() && cmpTerminate(l, r); } bool cmpExprStmt(const ExprStatement *l, const ExprStatement *r) const { return check(l->expression(), r->expression()); } bool cmpComma(const CommaExpr *l, const CommaExpr *r) const { return cmpNodeVector(l->expressions(), r->expressions()); } bool cmpIncExpr(const IncExpr *l, const IncExpr *r) const { if (l->form() != r->form()) return false; if (l->diff() != r->diff()) return false; return check(l->argument(), r->argument()); } bool cmpCallExpr(const CallExpr *l, const CallExpr *r) const { if (l->isNullable() != r->isNullable()) return false; if (!check(l->callee(), r->callee())) return false; return cmpNodeVector(l->arguments(), r->arguments()); } bool cmpArrayExpr(const ArrayExpr *l, const ArrayExpr *r) const { return cmpNodeVector(l->initializers(), r->initializers()); } bool cmpGetField(const GetFieldExpr *l, const GetFieldExpr *r) const { if (l->isNullable() != r->isNullable()) return false; if (strcmp(l->fieldName(), r->fieldName()) != 0) return false; return check(l->receiver(), r->receiver()); } bool cmpGetSlot(const GetSlotExpr *l, const GetSlotExpr *r) const { if (l->isNullable() != r->isNullable()) return false; if (!check(l->key(), r->key())) return false; return check(l->receiver(), r->receiver()); } bool cmpValueDecl(const ValueDecl *l, const ValueDecl *r) const { if (!check(l->expression(), r->expression())) return false; return strcmp(l->name(), r->name()) == 0; } bool cmpVarDecl(const VarDecl *l, const VarDecl *r) const { return l->isAssignable() == r->isAssignable() && cmpValueDecl(l, r); } bool cmpConst(const ConstDecl *l, const ConstDecl *r) const { if (l->isGlobal() != r->isGlobal()) return false; if (strcmp(l->name(), r->name()) != 0) return false; return check(l->value(), r->value()); } bool cmpDeclGroup(const DeclGroup *l, const DeclGroup *r) const { return cmpNodeVector(l->declarations(), r->declarations()); } bool cmpDestructDecl(const DestructuringDecl *l, const DestructuringDecl *r) const { return l->type() == r->type() && check(l->initExpression(), r->initExpression()); } bool cmpFunction(const FunctionExpr *l, const FunctionExpr *r) const { if (l->isVararg() != r->isVararg()) return false; if (l->isLambda() != r->isLambda()) return false; if (strcmp(l->name(), r->name()) != 0) return false; if (!cmpNodeVector(l->parameters(), r->parameters())) return false; return check(l->body(), r->body()); } bool cmpTable(const TableExpr *l, const TableExpr *r) const { const auto &lmems = l->members(); const auto &rmems = r->members(); if (lmems.size() != rmems.size()) return false; for (int32_t i = 0; i < lmems.size(); ++i) { const auto &lm = lmems[i]; const auto &rm = rmems[i]; if (!check(lm.key, rm.key)) return false; if (!check(lm.value, rm.value)) return false; if (lm.flags != rm.flags) return false; } return true; } bool cmpClass(const ClassExpr *l, const ClassExpr *r) const { if (!check(l->classBase(), r->classBase())) return false; if (!check(l->classKey(), r->classKey())) return false; return cmpTable(l, r); } bool cmpEnumDecl(const EnumDecl *l, const EnumDecl *r) const { if (l->isGlobal() != r->isGlobal()) return false; if (strcmp(l->name(), r->name()) != 0) return false; const auto &lcs = l->consts(); const auto &rcs = r->consts(); if (lcs.size() != rcs.size()) return false; for (int32_t i = 0; i < lcs.size(); ++i) { const auto &lc = lcs[i]; const auto &rc = rcs[i]; if (strcmp(lc.id, rc.id) != 0) return false; if (!cmpLiterals(lc.val, rc.val)) return false; } return true; } public: bool check(const Node *lhs, const Node *rhs) const { if (lhs == rhs) return true; if (!lhs || !rhs) return false; if (lhs->op() != rhs->op()) return false; switch (lhs->op()) { case TO_BLOCK: return cmpBlock((const Block *)lhs, (const Block *)rhs); case TO_IF: return cmpIf((const IfStatement *)lhs, (const IfStatement *)rhs); case TO_WHILE: return cmpWhile((const WhileStatement *)lhs, (const WhileStatement *)rhs); case TO_DOWHILE: return cmpDoWhile((const DoWhileStatement *)lhs, (const DoWhileStatement *)rhs); case TO_FOR: return cmpFor((const ForStatement *)lhs, (const ForStatement *)rhs); case TO_FOREACH: return cmpForeach((const ForeachStatement *)lhs, (const ForeachStatement *)rhs); case TO_SWITCH: return cmpSwitch((const SwitchStatement *)lhs, (const SwitchStatement *)rhs); case TO_RETURN: return cmpReturn((const ReturnStatement *)lhs, (const ReturnStatement *)rhs); case TO_YIELD: case TO_THROW: return cmpTerminate((const TerminateStatement *)lhs, (const TerminateStatement *)rhs); case TO_TRY: return cmpTry((const TryStatement *)lhs, (const TryStatement *)rhs); case TO_BREAK: case TO_CONTINUE: case TO_EMPTY: case TO_BASE: case TO_ROOT_TABLE_ACCESS: return true; case TO_EXPR_STMT: return cmpExprStmt((const ExprStatement *)lhs, (const ExprStatement *)rhs); //case TO_STATEMENT_MARK: case TO_ID: return cmpId((const Id *)lhs, (const Id *)rhs); case TO_COMMA: return cmpComma((const CommaExpr *)lhs, (const CommaExpr *)rhs); case TO_NULLC: case TO_ASSIGN: case TO_OROR: case TO_ANDAND: case TO_OR: case TO_XOR: case TO_AND: case TO_NE: case TO_EQ: case TO_3CMP: case TO_GE: case TO_GT: case TO_LE: case TO_LT: case TO_IN: case TO_INSTANCEOF: case TO_USHR: case TO_SHR: case TO_SHL: case TO_MUL: case TO_DIV: case TO_MOD: case TO_ADD: case TO_SUB: case TO_NEWSLOT: case TO_PLUSEQ: case TO_MINUSEQ: case TO_MULEQ: case TO_DIVEQ: case TO_MODEQ: return cmpBinary((const BinExpr *)lhs, (const BinExpr *)rhs); case TO_NOT: case TO_BNOT: case TO_NEG: case TO_TYPEOF: case TO_STATIC_MEMO: case TO_INLINE_CONST: case TO_RESUME: case TO_CLONE: case TO_PAREN: case TO_DELETE: return cmpUnary((const UnExpr *)lhs, (const UnExpr *)rhs); case TO_LITERAL: return cmpLiterals((const LiteralExpr *)lhs, (const LiteralExpr *)rhs); case TO_INC: return cmpIncExpr((const IncExpr *)lhs, (const IncExpr *)rhs); case TO_TABLE: return cmpTable((const TableExpr *)lhs, (const TableExpr *)rhs); case TO_CLASS: return cmpClass((const ClassExpr *)lhs, (const ClassExpr *)rhs); case TO_FUNCTION: return cmpFunction((const FunctionExpr *)lhs, (const FunctionExpr *)rhs); case TO_ARRAY: return cmpArrayExpr((const ArrayExpr *)lhs, (const ArrayExpr *)rhs); case TO_GETFIELD: return cmpGetField((const GetFieldExpr *)lhs, (const GetFieldExpr *)rhs); case TO_GETSLOT: return cmpGetSlot((const GetSlotExpr *)lhs, (const GetSlotExpr *)rhs); case TO_CALL: return cmpCallExpr((const CallExpr *)lhs, (const CallExpr *)rhs); case TO_TERNARY: return cmpTernary((const TerExpr *)lhs, (const TerExpr *)rhs); //case TO_EXPR_MARK: case TO_VAR: return cmpVarDecl((const VarDecl *)lhs, (const VarDecl *)rhs); case TO_PARAM: return cmpValueDecl((const ValueDecl *)lhs, (const ValueDecl *)rhs); case TO_CONST: return cmpConst((const ConstDecl *)lhs, (const ConstDecl *)rhs); case TO_DECL_GROUP: return cmpDeclGroup((const DeclGroup *)lhs, (const DeclGroup *)rhs); case TO_DESTRUCTURE: return cmpDestructDecl((const DestructuringDecl *)lhs, (const DestructuringDecl *)rhs); case TO_ENUM: return cmpEnumDecl((const EnumDecl *)lhs, (const EnumDecl *)rhs); case TO_SETFIELD: case TO_SETSLOT: default: assert(0); return false; } } }; } ================================================ FILE: squirrel/compiler/static_analyzer/operator_classification.h ================================================ #pragma once #include "compiler/ast.h" namespace SQCompilation { inline bool isSuspiciousNeighborOfNullCoalescing(TreeOp op) { return (op == TO_3CMP || op == TO_ANDAND || op == TO_OROR || op == TO_IN || /*op == TO_NOTIN ||*/ op == TO_EQ || op == TO_NE || op == TO_LE || op == TO_LT || op == TO_GT || op == TO_GE /*|| op == TO_NOT*/ || op == TO_BNOT || op == TO_AND || op == TO_OR || op == TO_XOR || op == TO_DIV || op == TO_MOD || op == TO_INSTANCEOF || /*op == TO_QMARK ||*/ /*op == TO_NEG ||*/ op == TO_ADD || op == TO_MUL || op == TO_SHL || op == TO_SHR || op == TO_USHR); } inline bool isSuspiciousTernaryConditionOp(TreeOp op) { return op == TO_ADD || op == TO_SUB || op == TO_MUL || op == TO_DIV || op == TO_MOD || op == TO_AND || op == TO_OR || op == TO_SHL || op == TO_SHR || op == TO_USHR || op == TO_3CMP || op == TO_NULLC; } inline bool isSuspiciousSameOperandsBinaryOp(TreeOp op) { return op == TO_EQ || op == TO_LE || op == TO_LT || op == TO_GE || op == TO_GT || op == TO_NE || op == TO_ANDAND || op == TO_OROR || op == TO_SUB || op == TO_3CMP || op == TO_DIV || op == TO_MOD || op == TO_OR || op == TO_AND || op == TO_XOR || op == TO_SHL || op == TO_SHR || op == TO_USHR; } inline bool isBlockTerminatorStatement(TreeOp op) { return op == TO_RETURN || op == TO_THROW || op == TO_BREAK || op == TO_CONTINUE; } inline bool isBooleanBinaryResultOperator(TreeOp op) { return op == TO_NE || op == TO_EQ || (TO_GE <= op && op <= TO_IN); } inline bool isBooleanResultOperator(TreeOp op) { return isBooleanBinaryResultOperator(op) || op == TO_NOT; } inline bool isArithOperator(TreeOp op) { return op == TO_OROR || op == TO_ANDAND || (TO_3CMP <= op && op <= TO_LT) || (TO_USHR <= op && op <= TO_SUB) || (TO_PLUSEQ <= op && op <= TO_MODEQ) || op == TO_BNOT || op == TO_NEG || op == TO_INC; } inline bool isDivOperator(TreeOp op) { return op == TO_DIV || op == TO_MOD || op == TO_DIVEQ || op == TO_MODEQ; } inline bool isPureArithOperator(TreeOp op) { return (TO_USHR <= op && op <= TO_SUB) || (TO_PLUSEQ <= op && op <= TO_MODEQ); } inline bool isRelationOperator(TreeOp op) { return TO_3CMP <= op && op <= TO_LT; } inline bool isBoolRelationOperator(TreeOp op) { return TO_GE <= op && op <= TO_LT; } inline bool isBitwiseOperator(TreeOp op) { return op == TO_OR || op == TO_AND || op == TO_XOR; } inline bool isBoolCompareOperator(TreeOp op) { return op == TO_NE || op == TO_EQ || isBoolRelationOperator(op); } inline bool isCompareOperator(TreeOp op) { return TO_NE <= op && op <= TO_LT; } inline bool isShiftOperator(TreeOp op) { return op == TO_SHL || op == TO_SHR || op == TO_USHR; } inline bool isHigherShiftPriority(TreeOp op) { return TO_MUL <= op && op <= TO_SUB; } inline bool isAssignOp(TreeOp op) { return op == TO_ASSIGN || (TO_PLUSEQ <= op && op <= TO_MODEQ); } } ================================================ FILE: squirrel/compiler/static_analyzer/symbol_info.h ================================================ #pragma once #include "compiler/ast.h" namespace SQCompilation { struct ImportInfo { int line; int column; const char *name; }; enum SymbolKind { SK_EXCEPTION, SK_FUNCTION, SK_CLASS, SK_TABLE, SK_VAR, SK_BINDING, SK_CONST, SK_ENUM, SK_ENUM_CONST, SK_PARAM, SK_FOREACH, SK_EXTERNAL_BINDING, SK_IMPORT }; const char *symbolContextName(SymbolKind k); struct SymbolInfo { union { const Id *x; const FunctionExpr *f; const ClassExpr *k; const TableMember *m; const VarDecl *v; const TableExpr *t; const ParamDecl *p; const EnumDecl *e; const ConstDecl *c; const EnumConst *ec; const ExternalValueExpr *ev; const ImportInfo *imp; } declarator; SymbolKind kind; bool declared; bool used; bool usedAfterAssign; const struct VarScope *ownedScope; SymbolInfo(SymbolKind k) : kind(k), declarator() { declared = true; used = usedAfterAssign = false; ownedScope = nullptr; } bool isConstant() const { return kind != SK_VAR; } const Node *extractPointedNode() const { switch (kind) { case SK_EXCEPTION: return declarator.x; case SK_FUNCTION: // Return the Id node if available (points to identifier for diagnostics) if (declarator.f && declarator.f->nameId()) return declarator.f->nameId(); return declarator.f; case SK_CLASS: return declarator.k; case SK_TABLE: return declarator.t; case SK_VAR: case SK_BINDING: case SK_FOREACH: // Return the Id node if available (points to identifier for diagnostics) if (declarator.v && declarator.v->nameId()) return declarator.v->nameId(); return declarator.v; case SK_CONST: return declarator.c; case SK_ENUM: return declarator.e; case SK_ENUM_CONST: return declarator.ec->val; case SK_PARAM: return declarator.p; case SK_EXTERNAL_BINDING: return declarator.ev; case SK_IMPORT: return nullptr; // Import slots don't have a Node representation default: assert(0); return nullptr; } } const char *contextName() const { return symbolContextName(kind); } }; } ================================================ FILE: squirrel/compiler/static_analyzer/value_ref.cpp ================================================ #include #include "value_ref.h" #include "node_equal_checker.h" namespace SQCompilation { static const ValueRefState mergeMatrix[VRS_NUM_OF_STATES][VRS_NUM_OF_STATES] = { // VRS_EXPRESSION VRS_INITIALIZED VRS_MULTIPLE VRS_UNKNOWN VRS_DECLARED { VRS_EXPRESSION, VRS_MULTIPLE, VRS_MULTIPLE, VRS_UNKNOWN, VRS_MULTIPLE }, // VRS_EXPRESSION { VRS_MULTIPLE, VRS_INITIALIZED, VRS_MULTIPLE, VRS_UNKNOWN, VRS_MULTIPLE }, // VRS_INITIALIZED { VRS_MULTIPLE, VRS_MULTIPLE, VRS_MULTIPLE, VRS_UNKNOWN, VRS_MULTIPLE }, // VRS_MULTIPLE { VRS_UNKNOWN, VRS_UNKNOWN, VRS_UNKNOWN, VRS_UNKNOWN, VRS_UNKNOWN }, // VRS_UNKNOWN { VRS_MULTIPLE, VRS_MULTIPLE, VRS_MULTIPLE, VRS_UNKNOWN, VRS_DECLARED } // VRS_DECLARED }; void ValueRef::intersectValue(const ValueRef *other) { assert(info == other->info); flagsPositive &= other->flagsPositive; flagsNegative &= other->flagsNegative; assert((flagsNegative & flagsPositive) == 0); assigned &= other->assigned; if (lastAssigneeScope) { if (other->lastAssigneeScope && other->lastAssigneeScope->depth > lastAssigneeScope->depth) { lastAssigneeScope = other->lastAssigneeScope; } } else { lastAssigneeScope = other->lastAssigneeScope; } // TODO: intersect bounds if (isConstant()) return; if (!NodeEqualChecker().check(expression, other->expression)) { kill(VRS_MULTIPLE, false); } } void ValueRef::merge(const ValueRef *other) { assert(info == other->info); assigned &= other->assigned; if (lastAssigneeScope) { if (other->lastAssigneeScope && other->lastAssigneeScope->depth > lastAssigneeScope->depth) { lastAssigneeScope = other->lastAssigneeScope; } } else { lastAssigneeScope = other->lastAssigneeScope; } if (state != other->state) { ValueRefState k = mergeMatrix[other->state][state]; kill(k); return; } if (isConstant()) { assert(other->isConstant()); return; } if (!NodeEqualChecker().check(expression, other->expression)) { kill(VRS_MULTIPLE); } } } ================================================ FILE: squirrel/compiler/static_analyzer/value_ref.h ================================================ #pragma once #include #include "var_scope.h" #include "symbol_info.h" namespace SQCompilation { struct SymbolInfo; class Expr; enum ValueRefState { VRS_EXPRESSION, VRS_INITIALIZED, VRS_MULTIPLE, VRS_UNKNOWN, VRS_DECLARED, VRS_NUM_OF_STATES }; struct ValueRef { SymbolInfo *info; enum ValueRefState state; const Expr *expression; /* used to track mixed assignments local x = 10 // eid = 1 let y = x // eid = 2 x = 20 // eid = 3 */ int32_t evalIndex; // Track aliasing: if this variable was initialized from another variable, // store a reference to it so we can check its current flags dynamically. const ValueRef *origin; int32_t originEvalIndex; // origin's evalIndex at the time of this declaration ValueRef(SymbolInfo *i, int32_t eid) : info(i), evalIndex(eid), state(), expression(nullptr) { assigned = false; lastAssigneeScope = nullptr; flagsPositive = flagsNegative = 0; origin = nullptr; originEvalIndex = -1; } bool hasValue() const { return state == VRS_EXPRESSION || state == VRS_INITIALIZED; } bool isConstant() const { return info->isConstant(); } bool assigned; const VarScope *lastAssigneeScope; unsigned flagsPositive, flagsNegative; void kill(ValueRefState k = VRS_UNKNOWN, bool clearFlags = true) { if (!isConstant()) { state = k; expression = nullptr; } if (clearFlags) { flagsPositive = 0; flagsNegative = 0; } } void intersectValue(const ValueRef *other); void merge(const ValueRef *other); }; } ================================================ FILE: squirrel/compiler/static_analyzer/var_scope.cpp ================================================ #include #include #include #include "var_scope.h" #include "value_ref.h" #include "symbol_info.h" #include "naming.h" #include "checker_visitor.h" #include "compiler/compilationcontext.h" #include "compiler/sourceloc.h" namespace SQCompilation { void VarScope::copyFrom(const VarScope *other) { VarScope *l = this; const VarScope *r = other; while (l) { assert(l->owner == r->owner && "Scope corruption"); auto &thisSymbols = l->symbols; auto &otherSymbols = r->symbols; auto it = otherSymbols.begin(); auto ie = otherSymbols.end(); while (it != ie) { thisSymbols[it->first] = it->second; ++it; } l = l->parent; r = r->parent; } } void VarScope::intersectScopes(const VarScope *other) { VarScope *l = this; const VarScope *r = other; while (l) { assert(l->owner == r->owner && "Scope corruption"); auto &thisSymbols = l->symbols; auto &otherSymbols = r->symbols; auto it = otherSymbols.begin(); auto ie = otherSymbols.end(); auto te = thisSymbols.end(); while (it != ie) { auto f = thisSymbols.find(it->first); if (f != te) { if (it->second->info == f->second->info) f->second->intersectValue(it->second); } ++it; } l = l->parent; r = r->parent; } evalId = std::max(evalId, other->evalId) + 1; // -V522 } void VarScope::mergeUnbalanced(const VarScope *other) { VarScope *lhs = this; const VarScope *rhs = other; while (lhs->depth > rhs->depth) { lhs = lhs->parent; } while (rhs->depth > lhs->depth) { rhs = rhs->parent; } lhs->merge(rhs); } void VarScope::merge(const VarScope *other) { VarScope *l = this; const VarScope *r = other; while (l) { assert(l->depth == r->depth && "Scope corruption"); assert(l->owner == r->owner && "Scope corruption"); auto &originSymbols = l->symbols; const auto &branchSymbols = r->symbols; for (const auto &kv : branchSymbols) { auto it = originSymbols.find(kv.first); if (it != originSymbols.end()) { // Lambdas declared on the same line could have same names if (it->second->info == kv.second->info) { it->second->merge(kv.second); } } // Otherwise (would be under `else`) the symbol only exists in branch // (e.g., lambda created in ternary) - it's local to that branch, // don't propagate to origin scope } l = l->parent; r = r->parent; } evalId = std::max(evalId, other->evalId) + 1; } VarScope *VarScope::findScope(const FunctionExpr *own) { VarScope *s = this; while (s) { if (s->owner == own) { return s; } s = s->parent; } return nullptr; } VarScope *VarScope::copy(Arena *a, bool forClosure) const { VarScope *parentCopy = parent ? parent->copy(a, forClosure) : nullptr; void *mem = a->allocate(sizeof(VarScope)); VarScope *thisCopy = new(mem) VarScope(owner, parentCopy); for (auto &kv : symbols) { const char *k = kv.first; ValueRef *v = kv.second; void *mem = a->allocate(sizeof(ValueRef)); ValueRef *vcopy = new(mem) ValueRef(v->info, v->evalIndex); if (!v->isConstant() && forClosure) { // if we analyze closure we cannot rely on existed assignable values vcopy->state = VRS_UNKNOWN; vcopy->expression = nullptr; vcopy->flagsNegative = vcopy->flagsPositive = 0; vcopy->assigned = v->assigned; vcopy->lastAssigneeScope = v->lastAssigneeScope; } else { memcpy(vcopy, v, sizeof(ValueRef)); vcopy->assigned = false; vcopy->lastAssigneeScope = nullptr; } thisCopy->symbols[k] = vcopy; } return thisCopy; } static SourceLoc getSymbolLocation(const SymbolInfo *info) { if (info->kind == SK_IMPORT) { const ImportInfo *imp = info->declarator.imp; return {imp->line, imp->column}; } const Node *node = info->extractPointedNode(); return {node->lineStart(), node->columnStart()}; //-V522 } void VarScope::checkUnusedSymbols(CheckerVisitor *checker) { std::vector> sorted(symbols.begin(), symbols.end()); std::sort(sorted.begin(), sorted.end(), [](const auto &a, const auto &b) { SourceLoc locA = getSymbolLocation(a.second->info); SourceLoc locB = getSymbolLocation(b.second->info); if (locA.line != locB.line) return locA.line < locB.line; return locA.column < locB.column; }); for (auto &s : sorted) { const char *n = s.first; const ValueRef *v = s.second; if (strcmp(n, "this") == 0 || n[0] == '@') continue; SymbolInfo *info = v->info; if (info->kind == SK_ENUM_CONST) continue; if (!info->used && n[0] != '_') { if (info->kind == SK_IMPORT) { const ImportInfo *import = info->declarator.imp; checker->reportImportSlot(import->line, import->column, import->name); } else { checker->report(info->extractPointedNode(), DiagnosticsId::DI_DECLARED_NEVER_USED, info->contextName(), n); // TODO: add hint for param/exception name about silencing it with '_' prefix } } else if (info->used && n[0] == '_') { if (info->kind == SK_PARAM || info->kind == SK_FOREACH) checker->report(info->extractPointedNode(), DiagnosticsId::DI_INVALID_UNDERSCORE, info->contextName(), n); } } } } ================================================ FILE: squirrel/compiler/static_analyzer/var_scope.h ================================================ #pragma once #include "compiler/ast.h" #include "analyzer_internal.h" namespace SQCompilation { struct ValueRef; class CheckerVisitor; struct VarScope { VarScope(const FunctionExpr *o, VarScope *p = nullptr) : owner(o), parent(p), depth(p ? p->depth + 1 : 0), evalId(p ? p->evalId + 1 : 0) {} ~VarScope() { if (parent) parent->~VarScope(); symbols.clear(); } int32_t evalId; const int32_t depth; const FunctionExpr *owner; VarScope *parent; std::unordered_map symbols; void intersectScopes(const VarScope *other); void merge(const VarScope *other); VarScope *copy(Arena *a, bool forClosure = false) const; void copyFrom(const VarScope *other); void mergeUnbalanced(const VarScope *other); VarScope *findScope(const FunctionExpr *own); void checkUnusedSymbols(CheckerVisitor *v); }; } ================================================ FILE: squirrel/compiler/typeinference.cpp ================================================ #include "sqpcheader.h" #ifndef NO_COMPILER #include "opcodes.h" #include "sqstring.h" #include "sqfuncproto.h" #include "sqfuncstate.h" #include "codegen.h" #include "sqvm.h" #include "sqtable.h" #include "sqclass.h" #include "sqclosure.h" #include "sqtypeparser.h" namespace SQCompilation { Expr *CodeGenVisitor::deparen(Expr *e) const { if (e->op() == TO_PAREN) return deparen(((UnExpr *)e)->argument()); else return e; } unsigned CodeGenVisitor::inferExprTypeMask(Expr *expr) { if (expr->isTypeInferred()) return expr->getTypeMask(); unsigned mask = inferExprTypeMaskImpl(expr); expr->setTypeMask(mask); expr->setTypeInferred(); return mask; } unsigned CodeGenVisitor::inferExprTypeMaskImpl(Expr *expr) { switch (expr->op()) { case TO_LITERAL: { LiteralExpr *lit = expr->asLiteral(); switch (lit->kind()) { case LK_INT: return _RT_INTEGER; case LK_FLOAT: return _RT_FLOAT; case LK_STRING: return _RT_STRING; case LK_BOOL: return _RT_BOOL; case LK_NULL: return _RT_NULL; } return ~0u; } case TO_TYPEOF: return _RT_STRING; case TO_NEG: { unsigned argMask = inferExprTypeMask(static_cast(expr)->argument()); if (argMask == ~0u) return ~0u; if (argMask & _RT_INSTANCE) return ~0u; // _unm metamethod return argMask & (_RT_INTEGER | _RT_FLOAT); } case TO_INC: { unsigned argMask = inferExprTypeMask(static_cast(expr)->argument()); if (argMask == ~0u) return ~0u; if (argMask & _RT_INSTANCE) return ~0u; // metamethods return argMask & (_RT_INTEGER | _RT_FLOAT); } // Comparisons -> bool case TO_EQ: case TO_NE: case TO_GT: case TO_GE: case TO_LT: case TO_LE: case TO_INSTANCEOF: case TO_IN: case TO_NOT: return _RT_BOOL; case TO_3CMP: // Bitwise -> integer case TO_AND: case TO_OR: case TO_XOR: case TO_SHL: case TO_SHR: case TO_USHR: case TO_BNOT: return _RT_INTEGER; // Arithmetic case TO_MUL: case TO_DIV: case TO_SUB: case TO_MOD: { BinExpr *bin = expr->asBinExpr(); unsigned lm = inferExprTypeMask(bin->lhs()); unsigned rm = inferExprTypeMask(bin->rhs()); if (lm == ~0u || rm == ~0u) return ~0u; if ((lm | rm) & _RT_INSTANCE) return ~0u; // _mul/_div/_sub/_modulo metamethods if ((lm & ~(_RT_INTEGER | _RT_FLOAT)) || (rm & ~(_RT_INTEGER | _RT_FLOAT))) return ~0u; // non-numeric operands if (lm == _RT_INTEGER && rm == _RT_INTEGER) return _RT_INTEGER; if ((lm & (_RT_INTEGER | _RT_FLOAT)) && (rm & (_RT_INTEGER | _RT_FLOAT))) { if ((lm & _RT_FLOAT) || (rm & _RT_FLOAT)) return _RT_FLOAT; return _RT_INTEGER | _RT_FLOAT; } return _RT_INTEGER | _RT_FLOAT; } case TO_ADD: { BinExpr *bin = expr->asBinExpr(); unsigned lm = inferExprTypeMask(bin->lhs()); unsigned rm = inferExprTypeMask(bin->rhs()); if (lm == ~0u || rm == ~0u) return ~0u; if ((lm | rm) & _RT_INSTANCE) return ~0u; // _add metamethod // String concat: if either side is definitely string, result is string if (lm == _RT_STRING || rm == _RT_STRING) return _RT_STRING; if ((lm & ~(_RT_INTEGER | _RT_FLOAT)) || (rm & ~(_RT_INTEGER | _RT_FLOAT))) return ~0u; // non-numeric/non-string operands if (lm == _RT_INTEGER && rm == _RT_INTEGER) return _RT_INTEGER; if ((lm & _RT_FLOAT) || (rm & _RT_FLOAT)) return _RT_FLOAT; return _RT_INTEGER | _RT_FLOAT; } // Logical: short-circuit, result is one of the operands case TO_OROR: case TO_ANDAND: { BinExpr *bin = expr->asBinExpr(); unsigned lm = inferExprTypeMask(bin->lhs()); unsigned rm = inferExprTypeMask(bin->rhs()); if (lm == ~0u || rm == ~0u) return ~0u; return lm | rm; } // Null-coalesce: lhs if non-null, else rhs case TO_NULLC: { BinExpr *bin = expr->asBinExpr(); unsigned lm = inferExprTypeMask(bin->lhs()); unsigned rm = inferExprTypeMask(bin->rhs()); if (lm == ~0u || rm == ~0u) return ~0u; return (lm & ~_RT_NULL) | rm; } // Ternary case TO_TERNARY: { TerExpr *ter = static_cast(expr); unsigned bm = inferExprTypeMask(ter->b()); unsigned cm = inferExprTypeMask(ter->c()); if (bm == ~0u || cm == ~0u) return ~0u; // Null-check narrowing: strip _RT_NULL from the appropriate branch // to avoid false positives with patterns like `x != null ? x : default` Expr *cond = deparen(ter->a()); if (cond->op() == TO_NE) { BinExpr *bin = cond->asBinExpr(); if ((bin->lhs()->op() == TO_LITERAL && bin->lhs()->asLiteral()->kind() == LK_NULL) || (bin->rhs()->op() == TO_LITERAL && bin->rhs()->asLiteral()->kind() == LK_NULL)) bm &= ~_RT_NULL; } else if (cond->op() == TO_EQ) { BinExpr *bin = cond->asBinExpr(); if ((bin->lhs()->op() == TO_LITERAL && bin->lhs()->asLiteral()->kind() == LK_NULL) || (bin->rhs()->op() == TO_LITERAL && bin->rhs()->asLiteral()->kind() == LK_NULL)) cm &= ~_RT_NULL; } return bm | cm; } // Identifiers: look up declared type case TO_ID: { Id *id = expr->asId(); SQObjectPtr nameObj(_fs->CreateString(id->name())); SQCompiletimeVarInfo varInfo; if (_fs->GetLocalVariable(nameObj, varInfo) != -1 || _fs->GetOuterVariable(nameObj, varInfo) != -1) { // If the variable was initialized with a known-type expression, use that if (varInfo.initializer && (varInfo.var_flags & (VF_ASSIGNABLE | VF_PARAM)) == 0) { unsigned initMask = inferExprTypeMask(varInfo.initializer); if (initMask != ~0u) { return initMask; } } // For functions initialized with pure calls, check the initializer if (varInfo.initializer && (varInfo.var_flags & VF_INIT_WITH_PURE)) { if (varInfo.initializer->op() == TO_CALL) { CallExpr *call = varInfo.initializer->asCallExpr(); Expr *callee = call->callee(); if (callee->op() == TO_ID) { SQObjectPtr calleeName(_fs->CreateString(callee->asId()->name())); SQCompiletimeVarInfo calleeInfo; if ((_fs->GetLocalVariable(calleeName, calleeInfo) != -1 || _fs->GetOuterVariable(calleeName, calleeInfo) != -1) && calleeInfo.initializer && calleeInfo.initializer->op() == TO_FUNCTION) { return calleeInfo.initializer->asFunctionExpr()->getResultTypeMask(); } } } } return varInfo.type_mask; } // Check named constants (const s = "err") SQObjectPtr constant; if (IsConstant(nameObj, constant)) { SQObjectType ctype = sq_type(constant); switch (ctype) { case OT_INTEGER: return _RT_INTEGER; case OT_FLOAT: return _RT_FLOAT; case OT_STRING: return _RT_STRING; case OT_BOOL: return _RT_BOOL; case OT_NULL: return _RT_NULL; case OT_ARRAY: return _RT_ARRAY; case OT_TABLE: return _RT_TABLE; case OT_CLOSURE: return _RT_CLOSURE; case OT_NATIVECLOSURE: return _RT_NATIVECLOSURE; case OT_CLASS: return _RT_CLASS; case OT_INSTANCE: return _RT_INSTANCE; default: return ~0u; } } return ~0u; } // Function call: try to determine return type case TO_CALL: { CallExpr *call = expr->asCallExpr(); Expr *callee = call->callee(); // freeze(x) returns the same type as x if (isFreezeCall(expr)) return inferExprTypeMask(call->arguments()[0]); unsigned retMask = ~0u; if (callee->op() == TO_ID) { SQObjectPtr calleeName(_fs->CreateString(callee->asId()->name())); SQCompiletimeVarInfo calleeInfo; if ((_fs->GetLocalVariable(calleeName, calleeInfo) != -1 || _fs->GetOuterVariable(calleeName, calleeInfo) != -1) && calleeInfo.initializer) { if (calleeInfo.initializer->op() == TO_FUNCTION) { retMask = calleeInfo.initializer->asFunctionExpr()->getResultTypeMask(); } else if (calleeInfo.initializer->op() == TO_CLASS) { retMask = _RT_INSTANCE; } } // Check named constants (const function [pure] ...) if (retMask == ~0u) { SQObjectPtr constant; if (IsConstant(calleeName, constant)) { if (sq_type(constant) == OT_CLOSURE) { SQClosure *cls = _closure(constant); if (cls->_function) retMask = cls->_function->_result_type_mask; } else if (sq_type(constant) == OT_NATIVECLOSURE) { SQNativeClosure *nc = _nativeclosure(constant); retMask = nc->_result_type_mask; } } } } else if (callee->op() == TO_FUNCTION) { retMask = callee->asFunctionExpr()->getResultTypeMask(); } if (call->isNullable() && retMask != ~0u) retMask |= _RT_NULL; return retMask; } // Container/type literals case TO_ARRAY: return _RT_ARRAY; case TO_CLASS: return _RT_CLASS; case TO_FUNCTION: return _RT_CLOSURE | _RT_NATIVECLOSURE; case TO_TABLE: case TO_ROOT_TABLE_ACCESS: return _RT_TABLE; case TO_PAREN: case TO_CLONE: // Static memo wraps an expression case TO_STATIC_MEMO: // Inline const wraps a literal case TO_INLINE_CONST: return inferExprTypeMask(static_cast(expr)->argument()); // Comma expression returns last value case TO_COMMA: { CommaExpr *comma = static_cast(expr); const auto &exprs = comma->expressions(); if (!exprs.empty()) return inferExprTypeMask(exprs.back()); return ~0u; } case TO_DELETE: case TO_RESUME: // Compound assign are complex; fall through to runtime case TO_PLUSEQ: case TO_MINUSEQ: case TO_MULEQ: case TO_DIVEQ: case TO_MODEQ: // Unknown at compile time case TO_GETFIELD: case TO_GETSLOT: case TO_SETFIELD: case TO_SETSLOT: case TO_BASE: case TO_ASSIGN: case TO_NEWSLOT: case TO_EXTERNAL_VALUE: case TO_CODE_BLOCK_EXPR: default: return ~0u; } } bool CodeGenVisitor::checkInferredType(Node *reportNode, Expr *expr, unsigned declaredMask) { #if SQ_RUNTIME_TYPE_CHECK if (declaredMask == ~0u) return false; unsigned inferredMask = inferExprTypeMask(expr); if (inferredMask != ~0u && (inferredMask & ~declaredMask) != 0) { char inferredBuf[160], declaredBuf[160]; sq_stringify_type_mask(inferredBuf, sizeof(inferredBuf), inferredMask); sq_stringify_type_mask(declaredBuf, sizeof(declaredBuf), declaredMask); reportDiagnostic(reportNode, DiagnosticsId::DI_INFERRED_TYPE_MISMATCH, inferredBuf, declaredBuf); return false; } return inferredMask != ~0u; #else (void)reportNode; (void)expr; (void)declaredMask; return false; #endif } } // namespace SQCompilation #endif // NO_COMPILER ================================================ FILE: squirrel/opcodes.h ================================================ /* see copyright notice in squirrel.h */ #ifndef _SQOPCODES_H_ #define _SQOPCODES_H_ #define MAX_FUNC_STACKSIZE 0xFF #define MAX_LITERALS ((SQInteger)0x7FFFFFFF) #define STATIC_MEMO_AUTO_FLAG 0x8000 #define STATIC_MEMO_IDX_MASK 0x7FFF enum BitWiseOP { BW_AND = 0, BW_OR = 2, BW_XOR = 3, BW_SHIFTL = 4, BW_SHIFTR = 5, BW_USHIFTR = 6 }; enum CmpOP { CMP_G = 0, CMP_GE = 2, CMP_L = 3, CMP_LE = 4, CMP_3W = 5 }; enum NewObjectType { NEWOBJ_TABLE = 0, NEWOBJ_ARRAY = 1, NEWOBJ_CLASS = 2 }; enum AppendArrayType { AAT_STACK = 0, AAT_LITERAL = 1, AAT_INT = 2, AAT_FLOAT = 3, AAT_BOOL = 4 }; #define SQ_OPCODES_LIST \ SQ_OPCODE(_OP_DATA_NOP) \ SQ_OPCODE(_OP_LOAD) \ SQ_OPCODE(_OP_LOADINT) \ SQ_OPCODE(_OP_LOADFLOAT) \ SQ_OPCODE(_OP_DLOAD) \ SQ_OPCODE(_OP_TAILCALL) \ SQ_OPCODE(_OP_CALL) \ SQ_OPCODE(_OP_PREPCALL) \ SQ_OPCODE(_OP_PREPCALLK) \ SQ_OPCODE(_OP_GETK) \ SQ_OPCODE(_OP_MOVE) \ SQ_OPCODE(_OP_NEWSLOT) \ SQ_OPCODE(_OP_NEWSLOTK) \ SQ_OPCODE(_OP_DELETE) \ SQ_OPCODE(_OP_SET) \ SQ_OPCODE(_OP_SETK)\ SQ_OPCODE(_OP_SETI)\ SQ_OPCODE(_OP_GET) \ SQ_OPCODE(_OP_SET_LITERAL) \ SQ_OPCODE(_OP_GET_LITERAL) \ SQ_OPCODE(_OP_EQ) \ SQ_OPCODE(_OP_NE) \ SQ_OPCODE(_OP_ADD) \ SQ_OPCODE(_OP_ADDI) \ SQ_OPCODE(_OP_SUB) \ SQ_OPCODE(_OP_MUL) \ SQ_OPCODE(_OP_DIV) \ SQ_OPCODE(_OP_MOD) \ SQ_OPCODE(_OP_BITW) \ SQ_OPCODE(_OP_RETURN) \ SQ_OPCODE(_OP_LOADNULLS) \ SQ_OPCODE(_OP_LOADROOT) \ SQ_OPCODE(_OP_LOADBOOL) \ SQ_OPCODE(_OP_DMOVE) \ SQ_OPCODE(_OP_JMP) \ SQ_OPCODE(_OP_JCMP) \ SQ_OPCODE(_OP_JCMPI) \ SQ_OPCODE(_OP_JCMPF) \ SQ_OPCODE(_OP_JCMPK) \ SQ_OPCODE(_OP_JZ) \ SQ_OPCODE(_OP_SETOUTER) \ SQ_OPCODE(_OP_GETOUTER) \ SQ_OPCODE(_OP_NEWOBJ) \ SQ_OPCODE(_OP_APPENDARRAY) \ SQ_OPCODE(_OP_COMPARITH) \ SQ_OPCODE(_OP_COMPARITH_K) \ SQ_OPCODE(_OP_INC) \ SQ_OPCODE(_OP_INCL) \ SQ_OPCODE(_OP_PINC) \ SQ_OPCODE(_OP_PINCL) \ SQ_OPCODE(_OP_CMP) \ SQ_OPCODE(_OP_EXISTS) \ SQ_OPCODE(_OP_INSTANCEOF) \ SQ_OPCODE(_OP_AND) \ SQ_OPCODE(_OP_OR) \ SQ_OPCODE(_OP_NEG) \ SQ_OPCODE(_OP_NOT) \ SQ_OPCODE(_OP_BWNOT) \ SQ_OPCODE(_OP_CLOSURE) \ SQ_OPCODE(_OP_YIELD) \ SQ_OPCODE(_OP_RESUME) \ SQ_OPCODE(_OP_PREFOREACH) \ SQ_OPCODE(_OP_POSTFOREACH) \ SQ_OPCODE(_OP_FOREACH) \ SQ_OPCODE(_OP_CLONE) \ SQ_OPCODE(_OP_TYPEOF) \ SQ_OPCODE(_OP_PUSHTRAP) \ SQ_OPCODE(_OP_POPTRAP) \ SQ_OPCODE(_OP_THROW) \ SQ_OPCODE(_OP_NEWSLOTA) \ SQ_OPCODE(_OP_GETBASE) \ SQ_OPCODE(_OP_CLOSE) \ SQ_OPCODE(_OP_NULLCOALESCE) \ SQ_OPCODE(_OP_NULLCALL) \ SQ_OPCODE(_OP_LOADCALLEE) \ SQ_OPCODE(_OP_PATCH_DOCOBJ) \ SQ_OPCODE(_OP_LOAD_STATIC_MEMO) \ SQ_OPCODE(_OP_SAVE_STATIC_MEMO) \ SQ_OPCODE(_OP_FREEZE) \ SQ_OPCODE(_OP_CHECK_TYPE) \ #define SQ_OPCODE(id) id, enum SQOpcode { SQ_OPCODES_LIST }; #undef SQ_OPCODE struct SQInstructionDesc { const char *name; }; struct SQInstruction { SQInstruction(){}; //-V730 SQInstruction(SQOpcode _op,SQInteger a0=0,SQInteger a1=0,SQInteger a2=0,SQInteger a3=0) { op = (unsigned char)_op; _arg0 = (unsigned char)a0;_arg1 = (SQInt32)a1; _arg2 = (unsigned char)a2;_arg3 = (unsigned char)a3; } SQInstruction(SQOpcode _op,SQInteger a0,float a1=0,SQInteger a2=0,SQInteger a3=0) { op = (unsigned char)_op; _arg0 = (unsigned char)a0;_farg1 = a1; _arg2 = (unsigned char)a2;_arg3 = (unsigned char)a3; } union { SQInt32 _arg1; float _farg1; }; unsigned char op; unsigned char _arg0; unsigned char _arg2; unsigned char _arg3; int _sarg0() const {return (signed char)_arg0;} int _sarg2() const {return (signed char)_arg2;} int _sarg3() const {return (signed char)_arg3;} }; #include "squtils.h" typedef sqvector SQInstructionVec; #define NEW_SLOT_STATIC_FLAG 0x02 #define OP_GET_FLAG_ALLOW_TYPE_METHODS 0x01 #define OP_GET_FLAG_NO_ERROR 0x02 #define OP_GET_FLAG_KEEP_VAL 0x04 //< only used with OP_GET_FLAG_NO_ERROR #define OP_GET_FLAG_TYPE_METHODS_ONLY 0x08 inline int sq_opcode_length(int op) { return (op == _OP_SET_LITERAL || op == _OP_GET_LITERAL) ? 2 : 1; } inline bool sq_is_pure_op(int op) { return op != _OP_SETOUTER && op != _OP_GETOUTER && op != _OP_SET_LITERAL && op != _OP_SET && op != _OP_SETI && op != _OP_SETK && op != _OP_CLOSURE && op != _OP_TAILCALL && op != _OP_CALL && op != _OP_NULLCALL && op != _OP_PREPCALL && op != _OP_PREPCALLK && op != _OP_NEWOBJ && op != _OP_APPENDARRAY && op != _OP_NEWSLOT && op != _OP_NEWSLOTK && op != _OP_NEWSLOTA && op != _OP_DELETE && op != _OP_YIELD && op != _OP_RESUME && op != _OP_CLONE && op != _OP_PUSHTRAP && op != _OP_POPTRAP && op != _OP_THROW && op != _OP_CLOSE && op != _OP_GETBASE; } #endif // _SQOPCODES_H_ ================================================ FILE: squirrel/sqapi.cpp ================================================ /* see copyright notice in squirrel.h */ #include "sqpcheader.h" #include "sqvm.h" #include "sqstring.h" #include "sqtable.h" #include "sqarray.h" #include "sqfuncproto.h" #include "sqclosure.h" #include "squserdata.h" #include "sqclass.h" #include "compiler/sqtypeparser.h" #include "compiler/compiler.h" #include "compiler/sqfuncstate.h" #include "compiler/arena.h" #include "compiler/ast.h" #include "ast_tools/ast_indent_render.h" #include "compiler/static_analyzer/analyzer.h" #include "compiler/static_analyzer/config.h" static_assert(sizeof(SQObject) == sizeof(SQObjectPtr), "SQObjectPtr must not add data members beyond SQObject"); SQUIRREL_API SQBool sq_tracevar(HSQUIRRELVM v, const HSQOBJECT *container, const HSQOBJECT * key, char * buf, int buf_size) { return v->GetVarTrace(SQObjectPtr(*container), SQObjectPtr(*key), buf, buf_size); } static bool sq_aux_gettypedarg(HSQUIRRELVM v,SQInteger idx,SQObjectType type,SQObjectPtr **o) { *o = &stack_get(v,idx); if(sq_type(**o) != type){ SQObjectPtr oval(v->PrintObjVal(**o)); v->Raise_Error("wrong argument type, expected '%s' got '%s'",IdType2Name(type),_stringval(oval)); return false; } return true; } #define _GETSAFE_OBJ(v,idx,type,o) { if(!sq_aux_gettypedarg(v,idx,type,&o)) return SQ_ERROR; } #define sq_aux_paramscheck(v,count) \ { \ if(sq_gettop(v) < count){ v->Raise_Error("not enough params in the stack"); return SQ_ERROR; }\ } using namespace SQCompilation; SQInteger sq_aux_invalidtype(HSQUIRRELVM v,SQObjectType type) { SQUnsignedInteger buf_size = 100 *sizeof(char); scsprintf(_ss(v)->GetScratchPad(buf_size), buf_size, "unexpected type %s", IdType2Name(type)); return sq_throwerror(v, _ss(v)->GetScratchPad(-1)); } HSQUIRRELVM sq_open(SQInteger initialstacksize) { SQAllocContext allocctx = nullptr; sq_vm_init_alloc_context(&allocctx); SQSharedState *ss = (SQSharedState *)SQ_MALLOC(allocctx, sizeof(SQSharedState)); new (ss) SQSharedState(allocctx); ss->Init(); SQVM *v = (SQVM *)SQ_MALLOC(allocctx, sizeof(SQVM)); new (v) SQVM(ss); ss->_root_vm = v; sq_vm_assign_to_alloc_context(allocctx, v); if(v->Init(NULL, initialstacksize)) { return v; } sq_delete(allocctx, v, SQVM); sq_vm_destroy_alloc_context(&allocctx); return NULL; } HSQUIRRELVM sq_newthread(HSQUIRRELVM friendvm, SQInteger initialstacksize) { SQSharedState *ss; SQVM *v; ss=_ss(friendvm); v= (SQVM *)SQ_MALLOC(ss->_alloc_ctx, sizeof(SQVM)); new (v) SQVM(ss); if(v->Init(friendvm, initialstacksize)) { friendvm->Push(SQObjectPtr(v)); return v; } else { sq_delete(ss->_alloc_ctx, v, SQVM); return NULL; } } SQInteger sq_getvmstate(HSQUIRRELVM v) { if(v->_suspended) return SQ_VMSTATE_SUSPENDED; else { if(v->_callsstacksize != 0) return SQ_VMSTATE_RUNNING; else return SQ_VMSTATE_IDLE; } } void sq_seterrorhandler(HSQUIRRELVM v) { SQObject o = stack_get(v, -1); if(sq_isclosure(o) || sq_isnativeclosure(o) || sq_isnull(o)) { v->_errorhandler = o; v->Pop(); } } HSQOBJECT sq_geterrorhandler(HSQUIRRELVM v) { return v->_errorhandler; } SQGETTHREAD sq_set_thread_id_function(HSQUIRRELVM v, SQGETTHREAD func) { SQGETTHREAD res = v->_get_current_thread_id_func; v->_get_current_thread_id_func = func; return res; } SQSQCALLHOOK sq_set_sq_call_hook(HSQUIRRELVM v, SQSQCALLHOOK hook) { SQSQCALLHOOK res = v->_sq_call_hook; v->_sq_call_hook = hook; return res; } SQWATCHDOGHOOK sq_set_watchdog_hook(HSQUIRRELVM v, SQWATCHDOGHOOK hook) { SQWATCHDOGHOOK res = v->_watchdog_hook; v->_watchdog_hook = hook; return res; } void sq_kick_watchdog(HSQUIRRELVM v) { if (v->_watchdog_hook) v->_watchdog_hook(v, true); } SQInteger sq_set_watchdog_timeout_msec(HSQUIRRELVM v, SQInteger timeout) { SQInteger prevTimeout = _ss(v)->watchdog_threshold_msec; _ss(v)->watchdog_threshold_msec = SQUnsignedInteger32(timeout < 0 ? 0 : timeout); sq_kick_watchdog(v); return prevTimeout; } void sq_forbidglobalconstrewrite(HSQUIRRELVM v, SQBool on) { if (on) _ss(v)->defaultLangFeatures |= LF_FORBID_GLOBAL_CONST_REWRITE; else _ss(v)->defaultLangFeatures &= ~LF_FORBID_GLOBAL_CONST_REWRITE; } void sq_setnativedebughook(HSQUIRRELVM v,SQDEBUGHOOK hook) { v->_debughook_native = hook; v->_debughook_closure.Null(); v->_debughook = hook?true:false; } void sq_setdebughook(HSQUIRRELVM v) { SQObject o = stack_get(v,-1); if(sq_isclosure(o) || sq_isnativeclosure(o) || sq_isnull(o)) { v->_debughook_closure = o; v->_debughook_native = NULL; v->_debughook = !sq_isnull(o); v->Pop(); } } SQCOMPILELINEHOOK sq_set_compile_line_hook(HSQUIRRELVM v, SQCOMPILELINEHOOK hook) { SQCOMPILELINEHOOK res = v->_compile_line_hook; v->_compile_line_hook = hook; return res; } void sq_close(HSQUIRRELVM v) { SQSharedState *ss = _ss(v); _thread(ss->_root_vm)->Finalize(); SQAllocContext allocctx = ss->_alloc_ctx; sq_delete(allocctx, ss, SQSharedState); sq_vm_destroy_alloc_context(&allocctx); } SQRESULT sq_compile(HSQUIRRELVM v, const char *s, SQInteger size, const char *sourcename, SQBool raiseerror, const HSQOBJECT *bindings) { SQObjectPtr o; #ifndef NO_COMPILER if (Compile(v, s, size, bindings, sourcename, o, raiseerror ? true : false)) { v->Push(SQObjectPtr(SQClosure::Create(_ss(v), _funcproto(o)))); return SQ_OK; } return SQ_ERROR; #else return sq_throwerror(v,"this is a no compiler build"); #endif } void sq_lineinfo_in_expressions(HSQUIRRELVM v, SQBool enable) { _ss(v)->_lineInfoInExpressions = enable ? true : false; } void sq_enablevartrace(HSQUIRRELVM v, SQBool enable) { _ss(v)->_varTraceEnabled = enable ? true : false; } SQBool sq_isvartracesupported() { #if SQ_VAR_TRACE_ENABLED == 1 return SQTrue; #else return SQFalse; #endif } void sq_setcompilationoption(HSQUIRRELVM v, enum CompilationOptions co, bool value) { if (value) _ss(v)->enableCompilationOption(co); else _ss(v)->disableCompilationOption(co); } bool sq_checkcompilationoption(HSQUIRRELVM v, enum CompilationOptions co) { return _ss(v)->checkCompilationOption(co); } void sq_notifyallexceptions(HSQUIRRELVM v, SQBool enable) { _ss(v)->_notifyallexceptions = enable?true:false; } void sq_addref_refcounted(HSQUIRRELVM v,HSQOBJECT *po) { assert(ISREFCOUNTED(sq_type(*po))); #ifdef NO_GARBAGE_COLLECTOR __AddRef(po->_type,po->_unVal); #else _ss(v)->_refs_table.AddRef(*po); #endif } SQUnsignedInteger sq_getrefcount(HSQUIRRELVM v,HSQOBJECT *po) { if(!ISREFCOUNTED(sq_type(*po))) return 0; #ifdef NO_GARBAGE_COLLECTOR return po->_unVal.pRefCounted->_uiRef; #else return _ss(v)->_refs_table.GetRefCount(*po); #endif } SQBool sq_release_refcounted(HSQUIRRELVM v,HSQOBJECT *po) { assert(ISREFCOUNTED(sq_type(*po))); #ifdef NO_GARBAGE_COLLECTOR bool ret = (po->_unVal.pRefCounted->_uiRef <= 1) ? SQTrue : SQFalse; __Release(po->_type,po->_unVal); return ret; //the ret val doesn't work(and cannot be fixed) #else return _ss(v)->_refs_table.Release(*po); #endif } SQUnsignedInteger sq_getvmrefcount(HSQUIRRELVM SQ_UNUSED_ARG(v), const HSQOBJECT *po) { if (!ISREFCOUNTED(sq_type(*po))) return 0; return po->_unVal.pRefCounted->_uiRef; } const char *sq_objtostring(const HSQOBJECT *o) { if(sq_type(*o) == OT_STRING) { return _stringval(*o); } return NULL; } SQInteger sq_objtointeger(const HSQOBJECT *o) { if(sq_isnumeric(*o)) { return tointeger(*o); } return 0; } SQFloat sq_objtofloat(const HSQOBJECT *o) { if(sq_isnumeric(*o)) { return tofloat(*o); } return 0; } SQBool sq_objtobool(const HSQOBJECT *o) { if(sq_isbool(*o)) { return _integer(*o); } return SQFalse; } SQBool sq_obj_is_true(const HSQOBJECT *o) { const SQObjectPtr &objPtr = static_cast(*o); return SQVM::IsFalse(objPtr) ? SQFalse : SQTrue; } SQUserPointer sq_objtouserpointer(const HSQOBJECT *o) { if(sq_isuserpointer(*o)) { return _userpointer(*o); } return 0; } SQRESULT sq_obj_getuserdata(const HSQOBJECT *obj, SQUserPointer *p, SQUserPointer *typetag) { if (obj->_type != OT_USERDATA) return SQ_ERROR; (*p) = _userdataval(*obj); if(typetag) *typetag = _userdata(*obj)->_typetag; return SQ_OK; } const char* sq_objtypestr(SQObjectType tp) { const char* s = IdType2Name(tp); return s ? s : ""; } void sq_getregistrytableobj(HSQUIRRELVM v, HSQOBJECT *out) { *out = _ss(v)->_registry; } SQRESULT sq_obj_get(HSQUIRRELVM v, const HSQOBJECT *obj, const HSQOBJECT *slot, HSQOBJECT *out, bool raw) { v->ValidateThreadAccess(); const SQObjectPtr &selfPtr = static_cast(*obj); const SQObjectPtr &slotPtr = static_cast(*slot); SQObjectPtr outPtr; SQUnsignedInteger getFlags = raw ? (GET_FLAG_DO_NOT_RAISE_ERROR | GET_FLAG_RAW) : GET_FLAG_DO_NOT_RAISE_ERROR; bool res = v->Get(selfPtr, slotPtr, outPtr, getFlags); if (res) { *out = outPtr; v->Push(outPtr); // keep result alive on VM stack; caller must sq_poptop() } return res ? SQ_OK : SQ_ERROR; } SQBool sq_obj_cmp(HSQUIRRELVM v, const HSQOBJECT *a, const HSQOBJECT *b, SQInteger *res) { const SQObjectPtr &aPtr = static_cast(*a); const SQObjectPtr &bPtr = static_cast(*b); return v->ObjCmp(aPtr, bPtr, *res); } bool sq_obj_is_equal(HSQUIRRELVM v, const HSQOBJECT *a, const HSQOBJECT *b) { return v->IsEqual(*a, *b); } SQInteger sq_obj_getsize(const HSQOBJECT *obj) { switch (obj->_type) { case OT_TABLE: return _table(*obj)->CountUsed(); case OT_ARRAY: return _array(*obj)->Size(); case OT_STRING: return _string(*obj)->_len; case OT_USERDATA: return _userdata(*obj)->_size; case OT_INSTANCE: return _instance(*obj)->_class->_udsize; case OT_CLASS: return _class(*obj)->_udsize; default: return SQ_ERROR; } } static SQRESULT getinstanceup_impl(SQInstance *inst, SQUserPointer *p, SQUserPointer typetag) { *p = inst->_userpointer; if (typetag != 0) { SQClass *cl = inst->_class; do { if (cl->_typetag == typetag) return SQ_OK; cl = cl->_base; } while (cl != NULL); return SQ_ERROR; } return SQ_OK; } SQRESULT sq_obj_getinstanceup(const HSQOBJECT *obj, SQUserPointer *p, SQUserPointer typetag) { if (obj->_type != OT_INSTANCE) return SQ_ERROR; return getinstanceup_impl(_instance(*obj), p, typetag); } // Shared raw-set logic for sq_rawset and sq_obj_set(raw=true). // Does NOT check immutability - callers handle that with their own error reporting. static SQRESULT rawset_impl(HSQUIRRELVM v, const SQObjectPtr &self, const SQObjectPtr &key, const SQObjectPtr &val) { switch (sq_type(self)) { case OT_TABLE: _table(self)->NewSlot(key, val); return SQ_OK; case OT_CLASS: _class(self)->NewSlot(_ss(v), key, val, false); return SQ_OK; case OT_INSTANCE: return _instance(self)->Set(key, val) == SLOT_STATUS_OK ? SQ_OK : SQ_ERROR; case OT_ARRAY: if (sq_isnumeric(key)) return _array(self)->Set(tointeger(key), val) ? SQ_OK : SQ_ERROR; return SQ_ERROR; default: return SQ_ERROR; } } SQRESULT sq_obj_set(HSQUIRRELVM v, const HSQOBJECT *obj, const HSQOBJECT *key, const HSQOBJECT *val, bool raw) { v->ValidateThreadAccess(); const SQObjectPtr &selfPtr = static_cast(*obj); const SQObjectPtr &keyPtr = static_cast(*key); const SQObjectPtr &valPtr = static_cast(*val); if (raw) { if (obj->_flags & SQOBJ_FLAG_IMMUTABLE) return SQ_ERROR; return rawset_impl(v, selfPtr, keyPtr, valPtr); } return v->Set(selfPtr, keyPtr, valPtr) ? SQ_OK : SQ_ERROR; } SQRESULT sq_obj_newslot(HSQUIRRELVM v, const HSQOBJECT *obj, const HSQOBJECT *key, const HSQOBJECT *val, bool bstatic) { v->ValidateThreadAccess(); SQObjectType tp = sq_type(*obj); if (tp != OT_TABLE && tp != OT_CLASS && tp != OT_INSTANCE) return SQ_ERROR; const SQObjectPtr &selfPtr = static_cast(*obj); const SQObjectPtr &keyPtr = static_cast(*key); const SQObjectPtr &valPtr = static_cast(*val); if (!v->NewSlot(selfPtr, keyPtr, valPtr, bstatic)) return SQ_ERROR; return SQ_OK; } #define MAX_FAST_COMPARE_SIZE 1024 static bool fastEqualByValue(const SQObjectPtr &a, const SQObjectPtr &b, int depth) { if (sq_type(a) != sq_type(b)) { if (!sq_isnumeric(a) || !sq_isnumeric(b)) return false; return tofloat(a) == tofloat(b); } // same type if (_rawval(a) == _rawval(b)) return true; if (depth <= 0) return false; if (sq_isarray(a)) { auto aa = _array(a); auto ab = _array(b); if (aa->Size() != ab->Size()) return false; if (aa->Size() > MAX_FAST_COMPARE_SIZE) return false; for (SQInteger i = 0; i < aa->Size(); i++) if (!fastEqualByValue(aa->_values[i], ab->_values[i], depth - 1)) return false; return true; } else if (sq_istable(a)) { auto ta = _table(a); auto tb = _table(b); if (ta->CountUsed() != tb->CountUsed()) return false; if (ta->CountUsed() > MAX_FAST_COMPARE_SIZE) return false; SQObjectPtr iter; while (true) { SQObjectPtr key, va, vb; iter._unVal.nInteger = ta->Next(true, iter, key, va); iter._type = OT_INTEGER; if (_integer(iter) < 0) break; if (!tb->Get(key, vb)) return false; if (!fastEqualByValue(va, vb, depth - 1)) return false; } return true; } return false; } bool sq_fast_equal_by_value_deep(const HSQOBJECT *a, const HSQOBJECT *b, int depth) { return fastEqualByValue( static_cast(*a), static_cast(*b), depth); } void sq_pushnull(HSQUIRRELVM v) { v->PushNull(); } void sq_pushstring(HSQUIRRELVM v,const char *s,SQInteger len) { if(s || len==0) v->Push(SQObjectPtr(SQString::Create(_ss(v), s, len))); else v->PushNull(); } void sq_pushinteger(HSQUIRRELVM v,SQInteger n) { v->Push(SQObjectPtr(n)); } void sq_pushbool(HSQUIRRELVM v,SQBool b) { v->Push(SQObjectPtr(b?true:false)); } void sq_pushfloat(HSQUIRRELVM v,SQFloat n) { v->Push(SQObjectPtr(n)); } void sq_pushuserpointer(HSQUIRRELVM v,SQUserPointer p) { v->Push(SQObjectPtr(p)); } void sq_pushthread(HSQUIRRELVM v, HSQUIRRELVM thread) { v->Push(SQObjectPtr(thread)); } SQUserPointer sq_newuserdata(HSQUIRRELVM v,SQUnsignedInteger size) { v->ValidateThreadAccess(); SQUserData *ud = SQUserData::Create(_ss(v), size + SQ_ALIGNMENT); v->Push(SQObjectPtr(ud)); return (SQUserPointer)sq_aligning(ud + 1); } void sq_newtable(HSQUIRRELVM v) { v->ValidateThreadAccess(); v->Push(SQObjectPtr(SQTable::Create(_ss(v), 0))); } void sq_newtableex(HSQUIRRELVM v,SQInteger initialcapacity) { v->Push(SQObjectPtr(SQTable::Create(_ss(v), initialcapacity))); } void sq_newarray(HSQUIRRELVM v,SQInteger size) { v->Push(SQObjectPtr(SQArray::Create(_ss(v), size))); } SQRESULT sq_newclass(HSQUIRRELVM v,SQBool hasbase) { SQClass *baseclass = NULL; if(hasbase) { SQObjectPtr &base = stack_get(v,-1); if(sq_type(base) != OT_CLASS) return sq_throwerror(v,"invalid base type"); baseclass = _class(base); } SQClass *newclass = SQClass::Create(v, baseclass); if(baseclass) v->Pop(); if (newclass) { v->Push(SQObjectPtr(newclass)); return SQ_OK; } else return SQ_ERROR; // propagate the raised error } SQBool sq_instanceof(HSQUIRRELVM v) { SQObjectPtr &obj = stack_get(v,-1); SQObjectPtr &cl = stack_get(v,-2); if(sq_type(cl) != OT_CLASS) return sq_throwerror(v,"invalid param type"); SQClass *cls = _class(cl); bool result = false; if (sq_type(obj) == OT_INSTANCE) { result = _instance(obj)->InstanceOf(cls); } else if (cls->_is_builtin_type) { SQObjectType obj_type = sq_type(obj); // both closures and native closures map to Function class if (cls->_builtin_type_id == OT_CLOSURE && (obj_type == OT_CLOSURE || obj_type == OT_NATIVECLOSURE)) { result = true; } else { result = (obj_type == cls->_builtin_type_id); } } return result ? SQTrue : SQFalse; } SQRESULT sq_arrayappend(HSQUIRRELVM v,SQInteger idx) { v->ValidateThreadAccess(); sq_aux_paramscheck(v,2); SQObjectPtr *arr; _GETSAFE_OBJ(v, idx, OT_ARRAY,arr); _array(*arr)->Append(v->GetUp(-1)); v->Pop(); return SQ_OK; } SQRESULT sq_arraypop(HSQUIRRELVM v,SQInteger idx,SQBool pushval) { v->ValidateThreadAccess(); sq_aux_paramscheck(v, 1); SQObjectPtr *arr; _GETSAFE_OBJ(v, idx, OT_ARRAY,arr); if(_array(*arr)->Size() > 0) { if(pushval != 0){ v->Push(_array(*arr)->Top()); } _array(*arr)->Pop(); return SQ_OK; } return sq_throwerror(v, "empty array"); } SQRESULT sq_arrayresize(HSQUIRRELVM v,SQInteger idx,SQInteger newsize) { v->ValidateThreadAccess(); sq_aux_paramscheck(v,1); SQObjectPtr *arr; _GETSAFE_OBJ(v, idx, OT_ARRAY,arr); if(newsize >= 0) { _array(*arr)->Resize(newsize); return SQ_OK; } return sq_throwerror(v,"negative size"); } SQRESULT sq_arrayreverse(HSQUIRRELVM v,SQInteger idx) { v->ValidateThreadAccess(); sq_aux_paramscheck(v, 1); SQObjectPtr *o; _GETSAFE_OBJ(v, idx, OT_ARRAY,o); SQArray *arr = _array(*o); if(arr->Size() > 0) { SQObjectPtr t; SQInteger size = arr->Size(); SQInteger n = size >> 1; size -= 1; for(SQInteger i = 0; i < n; i++) { t = arr->_values[i]; arr->_values[i] = arr->_values[size-i]; arr->_values[size-i] = t; } return SQ_OK; } return SQ_OK; } SQRESULT sq_arrayremove(HSQUIRRELVM v,SQInteger idx,SQInteger itemidx) { v->ValidateThreadAccess(); sq_aux_paramscheck(v, 1); SQObjectPtr *arr; _GETSAFE_OBJ(v, idx, OT_ARRAY,arr); return _array(*arr)->Remove(itemidx) ? SQ_OK : sq_throwerror(v,"index out of range"); } SQRESULT sq_arrayinsert(HSQUIRRELVM v,SQInteger idx,SQInteger destpos) { sq_aux_paramscheck(v, 1); SQObjectPtr *arr; _GETSAFE_OBJ(v, idx, OT_ARRAY,arr); SQRESULT ret = _array(*arr)->Insert(destpos, v->GetUp(-1)) ? SQ_OK : sq_throwerror(v,"index out of range"); v->Pop(); return ret; } void sq_newclosure(HSQUIRRELVM v,SQFUNCTION func,SQUnsignedInteger nfreevars) { SQNativeClosure *nc = SQNativeClosure::Create(_ss(v), func,nfreevars); nc->_nparamscheck = 0; for(SQUnsignedInteger i = 0; i < nfreevars; i++) { nc->_outervalues[i] = v->Top(); v->Pop(); } v->Push(SQObjectPtr(nc)); } SQRESULT sq_getclosureinfo(HSQUIRRELVM v,SQInteger idx,SQInteger *nparams,SQInteger *nfreevars) { SQObject o = stack_get(v, idx); if(sq_type(o) == OT_CLOSURE) { SQClosure *c = _closure(o); SQFunctionProto *proto = c->_function; *nparams = proto->_nparameters; *nfreevars = proto->_noutervalues; return SQ_OK; } else if(sq_type(o) == OT_NATIVECLOSURE) { SQNativeClosure *c = _nativeclosure(o); *nparams = c->_nparamscheck; *nfreevars = (SQInteger)c->_noutervalues; return SQ_OK; } return sq_throwerror(v,"the object is not a closure"); } SQRESULT sq_setnativeclosurename(HSQUIRRELVM v,SQInteger idx,const char *name) { SQObject o = stack_get(v, idx); if(sq_isnativeclosure(o)) { SQNativeClosure *nc = _nativeclosure(o); nc->_name = SQString::Create(_ss(v),name); return SQ_OK; } return sq_throwerror(v,"the object is not a nativeclosure"); } SQRESULT sq_setnativeclosuredocstring(HSQUIRRELVM v,SQInteger idx,const char *docstring) { assert(docstring); SQObject o = stack_get(v, idx); if(sq_isnativeclosure(o)) { SQObjectPtr docValue(SQString::Create(_ss(v), docstring)); SQObjectPtr docKey; docKey._type = OT_USERPOINTER; docKey._unVal.pUserPointer = (void *)_nativeclosure(o)->_function; _table(_ss(v)->doc_objects)->NewSlot(docKey, docValue); return SQ_OK; } return sq_throwerror(v,"the object is not a nativeclosure"); } SQRESULT sq_setobjectdocstring(HSQUIRRELVM v, const HSQOBJECT *obj, const char *docstring) { assert(obj); assert(docstring); if (sq_isclass(*obj) || sq_istable(*obj) || sq_isnativeclosure(*obj) || sq_isclosure(*obj)) { SQObjectPtr docValue(SQString::Create(_ss(v), docstring)); SQObjectPtr docKey; docKey._type = OT_USERPOINTER; docKey._unVal.pUserPointer = sq_isclass(*obj) || sq_istable(*obj) ? (void *)_userpointer(*obj) : sq_isnativeclosure(*obj) ? (void *)_nativeclosure(*obj)->_function : sq_isclosure(*obj) ? (void *)_closure(*obj)->_function : NULL; _table(_ss(v)->doc_objects)->NewSlot(docKey, docValue); return SQ_OK; } return sq_throwerror(v,"the object is not a table, class or function"); } SQRESULT sq_setparamscheck(HSQUIRRELVM v,SQInteger nparamscheck,const char *typemask) { SQObject o = stack_get(v, -1); if(!sq_isnativeclosure(o)) return sq_throwerror(v, "native closure expected"); SQNativeClosure *nc = _nativeclosure(o); nc->_nparamscheck = nparamscheck; if(typemask) { SQIntVec res(_ss(v)->_alloc_ctx); if(!CompileTypemask(res, typemask)) return sq_throwerror(v, "invalid typemask"); nc->_typecheck.copy(res); } else { nc->_typecheck.resize(0); } if(nparamscheck == SQ_MATCHTYPEMASKSTRING) { nc->_nparamscheck = nc->_typecheck.size(); } return SQ_OK; } SQRESULT sq_new_closure_slot_from_decl_string(HSQUIRRELVM v, SQFUNCTION func, SQUnsignedInteger nfreevars, const char *function_decl, const char *docstring) { // instead of: // sq_pushstring(v, function_name, -1); // sq_newclosure(v, func_ptr,0); // sq_setparamscheck(v, nparamscheck, typemask); // sq_setnativeclosurename(v, -1, name); // sq_newslot(v, -3, SQFalse); SQNativeClosure *nc = SQNativeClosure::Create(_ss(v), func, nfreevars); for(SQUnsignedInteger i = 0; i < nfreevars; i++) { nc->_outervalues[i] = v->Top(); v->Pop(); } SQFunctionType ft(_ss(v)); SQInteger error_pos = 0; SQObjectPtr error_string; if (!sq_parse_function_type_string(v, function_decl, ft, error_pos, error_string)) { SQPRINTFUNCTION printErrorFunc = sq_geterrorfunc(v); assert(printErrorFunc && "set 'print error function' using sq_setprintfunc()"); if (printErrorFunc) { printErrorFunc(v, "%s at position %d of type '%s'\n", _stringval(error_string), int(error_pos), function_decl); } assert(0 && "Invalid function type string, see error message in the log"); return sq_throwerror(v, "invalid function type string"); } sq_pushstring(v, _stringval(ft.functionName), -1); nc->_name = ft.functionName; nc->_purefunction = ft.pure; nc->_nodiscard = ft.nodiscard; nc->_typecheck.resize(ft.argTypeMask.size() + 1); nc->_typecheck[0] = ft.objectTypeMask; for (SQInteger i = 0; i < ft.argTypeMask.size(); i++) nc->_typecheck[i + 1] = ft.argTypeMask[i]; if (ft.ellipsisArgTypeMask != 0 || ft.requiredArgs != ft.argTypeMask.size()) nc->_nparamscheck = -ft.requiredArgs - 1; else nc->_nparamscheck = ft.requiredArgs + 1; nc->_result_type_mask = ft.returnTypeMask; #if SQ_STORE_DOC_OBJECTS if (docstring) { SQObjectPtr docValue(SQString::Create(_ss(v), docstring)); SQObjectPtr docKey; docKey._type = OT_USERPOINTER; docKey._unVal.pUserPointer = (void *)func; _table(_ss(v)->doc_objects)->NewSlot(docKey, docValue); } { SQObjectPtr declValue(SQString::Create(_ss(v), function_decl)); SQObjectPtr declKey; declKey._type = OT_USERPOINTER; declKey._unVal.pUserPointer = (void *)(((size_t)(void *)func) ^ ~size_t(0)); _table(_ss(v)->doc_objects)->NewSlot(declKey, declValue); } #else (void)(docstring); #endif v->Push(SQObjectPtr(nc)); //printf("%s: typecheck.size() = %d, nparamscheck = %d\n", function_decl, // int(nc->_typecheck.size()), int(nc->_nparamscheck)); return sq_newslot(v, -3, SQFalse); } SQRESULT sq_bindenv(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &o = stack_get(v,idx); if(!sq_isnativeclosure(o) && !sq_isclosure(o)) return sq_throwerror(v,"the target is not a closure"); SQObjectPtr &env = stack_get(v,-1); if(!sq_istable(env) && !sq_isarray(env) && !sq_isclass(env) && !sq_isinstance(env)) return sq_throwerror(v,"invalid environment"); SQWeakRef *w = _refcounted(env)->GetWeakRef(_ss(v)->_alloc_ctx, sq_type(env), env._flags); SQObjectPtr ret; if(sq_isclosure(o)) { SQClosure *c = _closure(o)->Clone(); __ObjRelease(c->_env); c->_env = w; __ObjAddRef(c->_env); if(_closure(o)->_base) { c->_base = _closure(o)->_base; __ObjAddRef(c->_base); } ret = c; } else { //then must be a native closure SQNativeClosure *c = _nativeclosure(o)->Clone(); __ObjRelease(c->_env); c->_env = w; __ObjAddRef(c->_env); ret = c; } v->Pop(); v->Push(ret); return SQ_OK; } SQRESULT sq_getclosurename(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &o = stack_get(v,idx); if(!sq_isnativeclosure(o) && !sq_isclosure(o)) return sq_throwerror(v,"the target is not a closure"); if(sq_isnativeclosure(o)) { v->Push(_nativeclosure(o)->_name); } else { //closure v->Push(_closure(o)->_function->_name); } return SQ_OK; } SQRESULT sq_clear(HSQUIRRELVM v,SQInteger idx, SQBool freemem) { SQObject &o=stack_get(v,idx); switch(sq_type(o)) { case OT_TABLE: _table(o)->Clear(freemem); break; case OT_ARRAY: _array(o)->Resize(0, freemem); break; default: return sq_throwerror(v, "clear only works on table and array"); break; } return SQ_OK; } void sq_pushroottable(HSQUIRRELVM v) { v->Push(v->_roottable); } void sq_pushregistrytable(HSQUIRRELVM v) { v->Push(_ss(v)->_registry); } void sq_pushconsttable(HSQUIRRELVM v) { v->Push(_ss(v)->_consts); } SQRESULT sq_setroottable(HSQUIRRELVM v) { SQObject o = stack_get(v, -1); if(sq_istable(o) || sq_isnull(o)) { v->_roottable = o; v->Pop(); return SQ_OK; } return sq_throwerror(v, "invalid type"); } SQRESULT sq_setconsttable(HSQUIRRELVM v) { SQObject o = stack_get(v, -1); if(sq_istable(o)) { _ss(v)->_consts = o; v->Pop(); return SQ_OK; } return sq_throwerror(v, "invalid type, expected table"); } void sq_setforeignptr(HSQUIRRELVM v,SQUserPointer p) { v->_foreignptr = p; } SQUserPointer sq_getforeignptr(HSQUIRRELVM v) { return v->_foreignptr; } void sq_setsharedforeignptr(HSQUIRRELVM v,SQUserPointer p) { _ss(v)->_foreignptr = p; } SQUserPointer sq_getsharedforeignptr(HSQUIRRELVM v) { return _ss(v)->_foreignptr; } void sq_setvmreleasehook(HSQUIRRELVM v,SQRELEASEHOOK hook) { v->_releasehook = hook; } SQRELEASEHOOK sq_getvmreleasehook(HSQUIRRELVM v) { return v->_releasehook; } void sq_setsharedreleasehook(HSQUIRRELVM v,SQRELEASEHOOK hook) { _ss(v)->_releasehook = hook; } SQRELEASEHOOK sq_getsharedreleasehook(HSQUIRRELVM v) { return _ss(v)->_releasehook; } void sq_push(HSQUIRRELVM v,SQInteger idx) { v->Push(stack_get(v, idx)); } SQObjectType sq_gettype(HSQUIRRELVM v,SQInteger idx) { return sq_type(stack_get(v, idx)); } SQRESULT sq_typeof(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &o = stack_get(v, idx); SQObjectPtr res; if(!v->TypeOf(o,res)) { return SQ_ERROR; } v->Push(res); return SQ_OK; } SQRESULT sq_tostring(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &o = stack_get(v, idx); SQObjectPtr res; if(!v->ToString(o,res)) { return SQ_ERROR; } v->Push(res); return SQ_OK; } void sq_tobool(HSQUIRRELVM v, SQInteger idx, SQBool *b) { SQObjectPtr &o = stack_get(v, idx); *b = SQVM::IsFalse(o)?SQFalse:SQTrue; } SQRESULT sq_getinteger(HSQUIRRELVM v,SQInteger idx,SQInteger *i) { SQObjectPtr &o = stack_get(v, idx); if(sq_isnumeric(o)) { *i = tointeger(o); return SQ_OK; } if(sq_isbool(o)) { *i = SQVM::IsFalse(o)?SQFalse:SQTrue; return SQ_OK; } return SQ_ERROR; } SQRESULT sq_getfloat(HSQUIRRELVM v,SQInteger idx,SQFloat *f) { SQObjectPtr &o = stack_get(v, idx); if(sq_isnumeric(o)) { *f = tofloat(o); return SQ_OK; } return SQ_ERROR; } SQRESULT sq_getbool(HSQUIRRELVM v,SQInteger idx,SQBool *b) { SQObjectPtr &o = stack_get(v, idx); if(sq_isbool(o)) { *b = _integer(o); return SQ_OK; } return SQ_ERROR; } SQRESULT sq_getstringandsize(HSQUIRRELVM v,SQInteger idx,const char **c,SQInteger *size) { SQObjectPtr *o = NULL; _GETSAFE_OBJ(v, idx, OT_STRING,o); *c = _stringval(*o); *size = _string(*o)->_len; return SQ_OK; } SQRESULT sq_getstring(HSQUIRRELVM v,SQInteger idx,const char **c) { SQObjectPtr *o = NULL; _GETSAFE_OBJ(v, idx, OT_STRING,o); *c = _stringval(*o); return SQ_OK; } SQRESULT sq_getthread(HSQUIRRELVM v,SQInteger idx,HSQUIRRELVM *thread) { SQObjectPtr *o = NULL; _GETSAFE_OBJ(v, idx, OT_THREAD,o); *thread = _thread(*o); return SQ_OK; } SQRESULT sq_clone(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &o = stack_get(v,idx); v->PushNull(); if(!v->Clone(o, stack_get(v, -1))){ v->Pop(); return SQ_ERROR; } return SQ_OK; } SQInteger sq_getsize(HSQUIRRELVM v, SQInteger idx) { SQObjectPtr &o = stack_get(v, idx); SQInteger res = sq_obj_getsize(&o); if (res == SQ_ERROR) return sq_aux_invalidtype(v, sq_type(o)); return res; } SQHash sq_gethash(HSQUIRRELVM v, SQInteger idx) { SQObjectPtr &o = stack_get(v, idx); return HashObj(o); } SQRESULT sq_getuserdata(HSQUIRRELVM v,SQInteger idx,SQUserPointer *p,SQUserPointer *typetag) { SQObjectPtr *o = NULL; _GETSAFE_OBJ(v, idx, OT_USERDATA,o); (*p) = _userdataval(*o); if(typetag) *typetag = _userdata(*o)->_typetag; return SQ_OK; } SQRESULT sq_settypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer typetag) { SQObjectPtr &o = stack_get(v,idx); switch(sq_type(o)) { case OT_USERDATA: _userdata(o)->_typetag = typetag; break; case OT_CLASS: _class(o)->_typetag = typetag; break; default: return sq_throwerror(v,"invalid object type"); } return SQ_OK; } SQRESULT sq_getobjtypetag(const HSQOBJECT *o,SQUserPointer * typetag) { switch(sq_type(*o)) { case OT_INSTANCE: *typetag = _instance(*o)->_class->_typetag; break; case OT_USERDATA: *typetag = _userdata(*o)->_typetag; break; case OT_CLASS: *typetag = _class(*o)->_typetag; break; default: return SQ_ERROR; } return SQ_OK; } SQRESULT sq_gettypetag(HSQUIRRELVM v,SQInteger idx,SQUserPointer *typetag) { SQObjectPtr &o = stack_get(v,idx); if (SQ_FAILED(sq_getobjtypetag(&o, typetag))) return SQ_ERROR;// this is not an error it should be a bool but would break backward compatibility return SQ_OK; } SQRESULT sq_getuserpointer(HSQUIRRELVM v, SQInteger idx, SQUserPointer *p) { SQObjectPtr *o = NULL; _GETSAFE_OBJ(v, idx, OT_USERPOINTER,o); (*p) = _userpointer(*o); return SQ_OK; } SQRESULT sq_setinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer p) { SQObjectPtr &o = stack_get(v,idx); if(sq_type(o) != OT_INSTANCE) return sq_throwerror(v,"the object is not a class instance"); _instance(o)->_userpointer = p; return SQ_OK; } SQRESULT sq_setclassudsize(HSQUIRRELVM v, SQInteger idx, SQInteger udsize) { SQObjectPtr &o = stack_get(v,idx); if(sq_type(o) != OT_CLASS) return sq_throwerror(v,"the object is not a class"); if(_class(o)->isLocked()) return sq_throwerror(v,"the class is locked"); _class(o)->_udsize = udsize; return SQ_OK; } SQRESULT sq_registernativefield(HSQUIRRELVM v, SQInteger classidx, const char *name, SQInteger offset, SQInteger fieldtype) { SQObjectPtr &o = stack_get(v, classidx); if (sq_type(o) != OT_CLASS) return sq_throwerror(v, "the object is not a class"); if (fieldtype < 0 || fieldtype > SQNFT_BOOL) return sq_throwerror(v, "invalid native field type"); if (offset < 0 || offset > 0xFFFF) return sq_throwerror(v, "native field offset out of range"); SQClass *cls = _class(o); if (cls->isLocked()) return sq_throwerror(v, "native field cannot be registered on a locked class"); if (cls->_udsize <= 0) return sq_throwerror(v, "native field requires inline userdata (call sq_setclassudsize first)"); SQObjectPtr key(SQString::Create(_ss(v), name)); if (!cls->RegisterNativeField(_ss(v), key, (uint16_t)offset, (uint8_t)fieldtype)) return sq_throwerror(v, "failed to register native field"); return SQ_OK; } SQRESULT sq_getinstanceup(HSQUIRRELVM v, SQInteger idx, SQUserPointer *p, SQUserPointer typetag) { SQObjectPtr &o = stack_get(v, idx); if (sq_type(o) != OT_INSTANCE) return sq_throwerror(v, "the object is not a class instance"); if (SQ_FAILED(getinstanceup_impl(_instance(o), p, typetag))) return sq_throwerror(v, "invalid type tag"); return SQ_OK; } SQInteger sq_gettop(HSQUIRRELVM v) { return (v->_top) - v->_stackbase; } void sq_settop(HSQUIRRELVM v, SQInteger newtop) { SQInteger top = sq_gettop(v); if(top > newtop) sq_pop(v, top - newtop); else while(top++ < newtop) sq_pushnull(v); } void sq_pop(HSQUIRRELVM v, SQInteger nelemstopop) { assert(v->_top >= nelemstopop); v->Pop(nelemstopop); } void sq_poptop(HSQUIRRELVM v) { assert(v->_top >= 1); v->Pop(); } void sq_remove(HSQUIRRELVM v, SQInteger idx) { v->Remove(idx); } SQInteger sq_cmp(HSQUIRRELVM v) { SQInteger res; v->ObjCmp(stack_get(v, -1), stack_get(v, -2),res); return res; } bool sq_cmpraw(HSQUIRRELVM v, HSQOBJECT &lhs, HSQOBJECT &rhs, SQInteger &res) { return v->ObjCmp(SQObjectPtr(lhs), SQObjectPtr(rhs), res); } SQRESULT sq_newslot(HSQUIRRELVM v, SQInteger idx, SQBool bstatic) { v->ValidateThreadAccess(); sq_aux_paramscheck(v, 3); SQObjectPtr &self = stack_get(v, idx); if(sq_type(self) == OT_TABLE || sq_type(self) == OT_CLASS) { SQObjectPtr &key = v->GetUp(-2); if (!v->NewSlot(self, key, v->GetUp(-1),bstatic?true:false)) return SQ_ERROR; v->Pop(2); return SQ_OK; } else { v->Raise_Error("cannot add slot to '%s'", GetTypeName(self)); return SQ_ERROR; } } SQRESULT sq_deleteslot(HSQUIRRELVM v,SQInteger idx,SQBool pushval) { v->ValidateThreadAccess(); sq_aux_paramscheck(v, 2); SQObjectPtr *self; _GETSAFE_OBJ(v, idx, OT_TABLE,self); SQObjectPtr &key = v->GetUp(-1); SQObjectPtr res; if(!v->DeleteSlot(*self, key, res)){ v->Pop(); return SQ_ERROR; } if(pushval) v->GetUp(-1) = res; else v->Pop(); return SQ_OK; } SQRESULT sq_set(HSQUIRRELVM v,SQInteger idx) { v->ValidateThreadAccess(); SQObjectPtr &self = stack_get(v, idx); if(v->Set(self, v->GetUp(-2), v->GetUp(-1))) { v->Pop(2); return SQ_OK; } return SQ_ERROR; } SQRESULT sq_rawset(HSQUIRRELVM v,SQInteger idx) { v->ValidateThreadAccess(); SQObjectPtr &self = stack_get(v, idx); if (self._flags & SQOBJ_FLAG_IMMUTABLE) { v->Raise_Error("trying to modify immutable '%s'",GetTypeName(self)); return SQ_ERROR; } if (SQ_SUCCEEDED(rawset_impl(v, self, v->GetUp(-2), v->GetUp(-1)))) { v->Pop(2); return SQ_OK; } SQObjectType tp = sq_type(self); if (tp == OT_TABLE || tp == OT_CLASS || tp == OT_INSTANCE || tp == OT_ARRAY) { v->Raise_IdxError(v->GetUp(-2)); return SQ_ERROR; } v->Pop(2); return sq_throwerror(v, "rawset works only on array/table/class and instance"); } SQRESULT sq_newmember(HSQUIRRELVM v,SQInteger idx,SQBool bstatic) { v->ValidateThreadAccess(); SQObjectPtr &self = stack_get(v, idx); if(sq_type(self) != OT_CLASS) return sq_throwerror(v, "new member only works with classes"); SQObjectPtr &key = v->GetUp(-2); if(!v->NewSlot(self,key,v->GetUp(-1),bstatic?true:false)) { v->Pop(2); return SQ_ERROR; } v->Pop(2); return SQ_OK; } SQRESULT sq_setdelegate(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &self = stack_get(v, idx); SQObjectPtr &mt = v->GetUp(-1); SQObjectType type = sq_type(self); switch(type) { case OT_TABLE: if(sq_type(mt) == OT_TABLE) { if(!_table(self)->SetDelegate(_table(mt))) { return sq_throwerror(v, "delagate cycle"); } v->Pop(); } else if(sq_type(mt)==OT_NULL) { _table(self)->SetDelegate(NULL); v->Pop(); } else return sq_aux_invalidtype(v,type); break; case OT_USERDATA: if(sq_type(mt)==OT_TABLE) { _userdata(self)->SetDelegate(_table(mt)); v->Pop(); } else if(sq_type(mt)==OT_NULL) { _userdata(self)->SetDelegate(NULL); v->Pop(); } else return sq_aux_invalidtype(v, type); break; default: return sq_aux_invalidtype(v, type); break; } return SQ_OK; } SQRESULT sq_rawdeleteslot(HSQUIRRELVM v,SQInteger idx,SQBool pushval) { v->ValidateThreadAccess(); sq_aux_paramscheck(v, 2); SQObjectPtr *self; _GETSAFE_OBJ(v, idx, OT_TABLE,self); if (self->_flags & SQOBJ_FLAG_IMMUTABLE) { v->Raise_Error("trying to modify immutable '%s'",GetTypeName(*self)); return SQ_ERROR; } SQObjectPtr &key = v->GetUp(-1); SQObjectPtr t; if(_table(*self)->Get(key,t)) { _table(*self)->Remove(key); } if(pushval != 0) v->GetUp(-1) = t; else v->Pop(); return SQ_OK; } SQRESULT sq_getdelegate(HSQUIRRELVM v,SQInteger idx) { v->ValidateThreadAccess(); SQObjectPtr &self=stack_get(v,idx); switch(sq_type(self)){ case OT_TABLE: case OT_USERDATA: if(!_delegable(self)->_delegate){ v->PushNull(); break; } v->Push(SQObjectPtr(_delegable(self)->_delegate)); break; default: return sq_throwerror(v,"wrong type"); break; } return SQ_OK; } SQRESULT sq_get(HSQUIRRELVM v,SQInteger idx) { const SQObjectPtr &self = stack_get(v,idx); SQObjectPtr &obj = v->GetUp(-1); if (v->Get(self,obj,obj,GET_FLAG_DO_NOT_RAISE_ERROR)) return SQ_OK; v->Pop(); return SQ_ERROR; } static inline void propagate_immutable(const SQObject &obj, SQObject &slot_val) { if (sq_objflags(obj) & SQOBJ_FLAG_IMMUTABLE) slot_val._flags |= SQOBJ_FLAG_IMMUTABLE; } SQRESULT sq_rawget(HSQUIRRELVM v, SQInteger idx) { v->ValidateThreadAccess(); const SQObjectPtr &self = stack_get(v, idx); SQObjectPtr &obj = v->GetUp(-1); if (v->Get(self, obj, obj, GET_FLAG_DO_NOT_RAISE_ERROR | GET_FLAG_RAW)) return SQ_OK; v->Pop(); return SQ_ERROR; } SQRESULT sq_getstackobj(HSQUIRRELVM v,SQInteger idx,HSQOBJECT *po) { *po=stack_get(v,idx); return SQ_OK; } const char *sq_getlocal(HSQUIRRELVM v,SQUnsignedInteger level,SQUnsignedInteger idx) { SQUnsignedInteger cstksize=v->_callsstacksize; SQUnsignedInteger lvl=(cstksize-level)-1; SQInteger stackbase=v->_stackbase; if(lvl_callsstack[(cstksize-i)-1]; stackbase-=ci._prevstkbase; } SQVM::CallInfo &ci=v->_callsstack[lvl]; if(sq_type(ci._closure)!=OT_CLOSURE) return NULL; SQClosure *c=_closure(ci._closure); SQFunctionProto *func=c->_function; if(func->_noutervalues > (SQInteger)idx) { v->Push(*_outer(c->_outervalues[idx])->_valptr); return _stringval(func->_outervalues[idx]._name); } idx -= func->_noutervalues; return func->GetLocal(v,stackbase,idx,(SQInteger)(ci._ip-func->_instructions)); } return NULL; } void sq_pushobj(HSQUIRRELVM v,const HSQOBJECT *po) { v->Push(SQObjectPtr(*po)); } void sq_throwparamtypeerror(HSQUIRRELVM v, SQInteger nparam, SQInteger typemask, SQInteger type) { v->Raise_ParamTypeError(nparam, typemask, type); } SQRESULT sq_throwerror(HSQUIRRELVM v,const char *err) { v->_lasterror=SQString::Create(_ss(v),err); return SQ_ERROR; } SQRESULT sq_throwobject(HSQUIRRELVM v) { v->_lasterror = v->GetUp(-1); v->Pop(); return SQ_ERROR; } void sq_reseterror(HSQUIRRELVM v) { v->_lasterror.Null(); } void sq_getlasterror(HSQUIRRELVM v) { v->Push(v->_lasterror); } SQRESULT sq_reservestack(HSQUIRRELVM v,SQInteger nsize) { if (((SQUnsignedInteger)v->_top + nsize) > v->_stack.size()) { if(v->_nmetamethodscall) { return sq_throwerror(v,"cannot resize stack while in a metamethod"); } v->_stack.resize(v->_stack.size() + ((v->_top + nsize) - v->_stack.size())); } return SQ_OK; } SQRESULT sq_resume(HSQUIRRELVM v,SQBool retval,SQBool invoke_err_handler) { if (sq_type(v->GetUp(-1)) == OT_GENERATOR) { v->PushNull(); //retval bool res = v->_debughook ? v->Execute(v->GetUp(-2), 0, v->_top, v->GetUp(-1), invoke_err_handler, SQVM::ET_RESUME_GENERATOR) : v->Execute(v->GetUp(-2), 0, v->_top, v->GetUp(-1), invoke_err_handler, SQVM::ET_RESUME_GENERATOR); if (!res) { v->Raise_Error(v->_lasterror); return SQ_ERROR; } if(!retval) v->Pop(); return SQ_OK; } return sq_throwerror(v,"only generators can be resumed"); } SQRESULT sq_call(HSQUIRRELVM v,SQInteger params,SQBool retval,SQBool invoke_err_handler) { v->ValidateThreadAccess(); if (v->_sq_call_hook) v->_sq_call_hook(v); SQObjectPtr res; if(!v->Call(v->GetUp(-(params+1)),params,v->_top-params,res,invoke_err_handler?true:false)){ v->Pop(params); //pop args return SQ_ERROR; } if(!v->_suspended) v->Pop(params); //pop args if(retval) v->Push(res); // push result return SQ_OK; } SQRESULT sq_tailcall(HSQUIRRELVM v, SQInteger nparams) { v->ValidateThreadAccess(); SQObjectPtr &res = v->GetUp(-(nparams + 1)); if (sq_type(res) != OT_CLOSURE) { return sq_throwerror(v, "only closure can be tail called"); } SQClosure *clo = _closure(res); if (clo->_function->_bgenerator) { return sq_throwerror(v, "generators cannot be tail called"); } SQInteger stackbase = (v->_top - nparams) - v->_stackbase; if (!v->TailCall(clo, stackbase, nparams)) { return SQ_ERROR; } return SQ_TAILCALL_FLAG; } SQRESULT sq_suspendvm(HSQUIRRELVM v) { return v->Suspend(); } SQRESULT sq_wakeupvm(HSQUIRRELVM v,SQBool wakeupret,SQBool retval,SQBool invoke_err_handler,SQBool throwerror) { SQObjectPtr ret; if(!v->_suspended) return sq_throwerror(v,"cannot resume a vm that is not running any code"); SQInteger target = v->_suspended_target; if(wakeupret) { if(target != -1) { v->GetAt(v->_stackbase+v->_suspended_target)=v->GetUp(-1); //retval } v->Pop(); } else if(target != -1) { v->GetAt(v->_stackbase+v->_suspended_target).Null(); } SQObjectPtr dummy; bool res = v->_debughook ? v->Execute(dummy,-1,-1,ret,invoke_err_handler,throwerror?SQVM::ET_RESUME_THROW_VM : SQVM::ET_RESUME_VM) : v->Execute(dummy,-1,-1,ret,invoke_err_handler,throwerror?SQVM::ET_RESUME_THROW_VM : SQVM::ET_RESUME_VM); if(!res) return SQ_ERROR; if(retval) v->Push(ret); return SQ_OK; } void sq_setreleasehook(HSQUIRRELVM v,SQInteger idx,SQRELEASEHOOK hook) { SQObjectPtr &ud=stack_get(v,idx); switch(sq_type(ud) ) { case OT_USERDATA: _userdata(ud)->_hook = hook; break; case OT_INSTANCE: _instance(ud)->_hook = hook; break; case OT_CLASS: _class(ud)->_hook = hook; break; default: return; } } SQRELEASEHOOK sq_getreleasehook(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr &ud=stack_get(v,idx); switch(sq_type(ud) ) { case OT_USERDATA: return _userdata(ud)->_hook; break; case OT_INSTANCE: return _instance(ud)->_hook; break; case OT_CLASS: return _class(ud)->_hook; break; default: return NULL; } } void sq_setcompilererrorhandler(HSQUIRRELVM v,SQCOMPILERERROR f) { _ss(v)->_compilererrorhandler = f; } SQCOMPILERERROR sq_getcompilererrorhandler(HSQUIRRELVM v) { return _ss(v)->_compilererrorhandler; } void sq_setcompilerdiaghandler(HSQUIRRELVM v, SQ_COMPILER_DIAG_CB f) { _ss(v)->_compilerdiaghandler = f; } SQRESULT sq_writeclosure(HSQUIRRELVM v,SQWRITEFUNC w,SQUserPointer up) { SQObjectPtr *o = NULL; _GETSAFE_OBJ(v, -1, OT_CLOSURE,o); unsigned short tag = SQ_BYTECODE_STREAM_TAG; if(_closure(*o)->_function->_noutervalues) return sq_throwerror(v,"a closure with free variables bound cannot be serialized"); if(w(up,&tag,2) != 2) return sq_throwerror(v,"io error"); if(!_closure(*o)->Save(v,up,w)) return SQ_ERROR; return SQ_OK; } SQRESULT sq_readclosure(HSQUIRRELVM v,SQREADFUNC r,SQUserPointer up) { SQObjectPtr closure; unsigned short tag; if(r(up,&tag,2) != 2) return sq_throwerror(v,"io error"); if(tag != SQ_BYTECODE_STREAM_TAG) return sq_throwerror(v,"invalid stream"); if(!SQClosure::Load(v,up,r,closure)) return SQ_ERROR; v->Push(closure); return SQ_OK; } char *sq_getscratchpad(HSQUIRRELVM v,SQInteger minsize) { return _ss(v)->GetScratchPad(minsize); } SQRESULT sq_resurrectunreachable(HSQUIRRELVM v) { #ifndef NO_GARBAGE_COLLECTOR _ss(v)->ResurrectUnreachable(v); return SQ_OK; #else return sq_throwerror(v,"sq_resurrectunreachable requires a garbage collector build"); #endif } SQInteger sq_collectgarbage(HSQUIRRELVM v) { #ifndef NO_GARBAGE_COLLECTOR return _ss(v)->CollectGarbage(v); #else return -1; #endif } SQRESULT sq_getcallee(HSQUIRRELVM v) { if(v->_callsstacksize > 1) { v->Push(v->_callsstack[v->_callsstacksize - 2]._closure); return SQ_OK; } return sq_throwerror(v,"no closure in the calls stack"); } const char *sq_getfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval) { SQObjectPtr &self=stack_get(v,idx); const char *name = NULL; switch(sq_type(self)) { case OT_CLOSURE:{ SQClosure *clo = _closure(self); SQFunctionProto *fp = clo->_function; if(((SQUnsignedInteger)fp->_noutervalues) > nval) { v->Push(*(_outer(clo->_outervalues[nval])->_valptr)); SQOuterVar &ov = fp->_outervalues[nval]; name = _stringval(ov._name); } } break; case OT_NATIVECLOSURE:{ SQNativeClosure *clo = _nativeclosure(self); if(clo->_noutervalues > nval) { v->Push(clo->_outervalues[nval]); name = "@NATIVE"; } } break; default: break; //shutup compiler } return name; } SQRESULT sq_setfreevariable(HSQUIRRELVM v,SQInteger idx,SQUnsignedInteger nval) { SQObjectPtr &self=stack_get(v,idx); switch(sq_type(self)) { case OT_CLOSURE:{ SQFunctionProto *fp = _closure(self)->_function; if(((SQUnsignedInteger)fp->_noutervalues) > nval){ *(_outer(_closure(self)->_outervalues[nval])->_valptr) = stack_get(v,-1); } else return sq_throwerror(v,"invalid free var index"); } break; case OT_NATIVECLOSURE: if(_nativeclosure(self)->_noutervalues > nval){ _nativeclosure(self)->_outervalues[nval] = stack_get(v,-1); } else return sq_throwerror(v,"invalid free var index"); break; default: return sq_aux_invalidtype(v, sq_type(self)); } v->Pop(); return SQ_OK; } SQRESULT sq_getmemberhandle(HSQUIRRELVM v,SQInteger idx,HSQMEMBERHANDLE *handle) { SQObjectPtr *o = NULL; _GETSAFE_OBJ(v, idx, OT_CLASS,o); SQObjectPtr &key = stack_get(v,-1); SQTable *m = _class(*o)->_members; SQObjectPtr val; if(m->Get(key,val)) { handle->_static = !(_isfield(val) || _isnativefield(val)); handle->_isNativeField = _isnativefield(val); handle->_index = _member_idx(val); v->Pop(); return SQ_OK; } return sq_throwerror(v,"wrong index"); } // Handles field and method access. Native fields are handled directly // in sq_getbyhandle/sq_setbyhandle before calling this. SQRESULT _getmemberbyhandle(HSQUIRRELVM v,SQObjectPtr &self,const HSQMEMBERHANDLE *handle,SQObjectPtr *&val) { switch(sq_type(self)) { case OT_INSTANCE: { SQInstance *i = _instance(self); if(handle->_static) { SQClass *c = i->_class; val = &c->_methods[handle->_index].val; } else { val = &i->_values[handle->_index]; } } break; case OT_CLASS: { SQClass *c = _class(self); if(handle->_static) { val = &c->_methods[handle->_index].val; } else { val = &c->_defaultvalues[handle->_index].val; } } break; default: return sq_throwerror(v,"wrong type(expected class or instance)"); } return SQ_OK; } SQRESULT sq_getbyhandle(HSQUIRRELVM v,SQInteger idx,const HSQMEMBERHANDLE *handle) { SQObjectPtr &self = stack_get(v,idx); if (handle->_isNativeField) { if (sq_type(self) != OT_INSTANCE) return sq_throwerror(v, "expected instance for native field"); SQObjectPtr tmp; _instance(self)->GetNativeField(handle->_index, tmp); propagate_immutable(self, tmp); v->Push(tmp); return SQ_OK; } else { SQObjectPtr *val = NULL; if(SQ_FAILED(_getmemberbyhandle(v,self,handle,val))) { return SQ_ERROR; } SQObjectPtr res(_realval(*val)); propagate_immutable(self, res); v->Push(res); return SQ_OK; } } SQRESULT sq_setbyhandle(HSQUIRRELVM v,SQInteger idx,const HSQMEMBERHANDLE *handle) { if (handle->_isNativeField) { SQObjectPtr &self = stack_get(v,idx); SQObjectPtr &newval = stack_get(v,-1); if (sq_type(self) != OT_INSTANCE) return sq_throwerror(v,"expected instance for native field"); if (!_instance(self)->SetNativeField(handle->_index, newval)) return sq_throwerror(v,"type mismatch in native field assignment"); v->Pop(); return SQ_OK; } SQObjectPtr &self = stack_get(v,idx); SQObjectPtr &newval = stack_get(v,-1); SQObjectPtr *val = NULL; if(SQ_FAILED(_getmemberbyhandle(v,self,handle,val))) { return SQ_ERROR; } *val = newval; v->Pop(); return SQ_OK; } SQRESULT sq_getbase(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr *o = NULL; _GETSAFE_OBJ(v, idx, OT_CLASS,o); if(_class(*o)->_base) v->Push(SQObjectPtr(_class(*o)->_base)); else v->PushNull(); return SQ_OK; } SQRESULT sq_getclass(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr *o = NULL; _GETSAFE_OBJ(v, idx, OT_INSTANCE,o); v->Push(SQObjectPtr(_instance(*o)->_class)); return SQ_OK; } SQRESULT sq_createinstance(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr *o = NULL; _GETSAFE_OBJ(v, idx, OT_CLASS,o); SQInstance *inst = _class(*o)->CreateInstance(v); if (!inst) return SQ_ERROR; v->Push(SQObjectPtr(inst)); return SQ_OK; } void sq_weakref(HSQUIRRELVM v,SQInteger idx) { const SQObjectPtr &o=stack_get(v,idx); if(ISREFCOUNTED(sq_type(o))) { v->Push(SQObjectPtr(_refcounted(o)->GetWeakRef(_ss(v)->_alloc_ctx, sq_type(o), o._flags))); return; } v->Push(o); } SQRESULT sq_getweakrefval(HSQUIRRELVM v,SQInteger idx) { const SQObjectPtr &o = stack_get(v,idx); if(sq_type(o) != OT_WEAKREF) { return sq_throwerror(v,"the object must be a weakref"); } v->Push(SQObjectPtr(_weakref(o)->_obj)); return SQ_OK; } SQRESULT sq_next(HSQUIRRELVM v,SQInteger idx) { SQObjectPtr o=stack_get(v,idx),&refpos = stack_get(v,-1),realkey,val; if(sq_type(o) == OT_GENERATOR) { return sq_throwerror(v,"cannot iterate a generator"); } int faketojump; if(!v->FOREACH_OP(o,realkey,val,refpos,666,faketojump)) return SQ_ERROR; if(faketojump != 666) { v->Push(realkey); v->Push(val); return SQ_OK; } return SQ_ERROR; } SQCompilation::SqASTData *sq_parsetoast(HSQUIRRELVM v, const char *s, SQInteger size, const char *sourcename, SQBool preserveComments, SQBool raiseerror) { return ParseToAST(v, s, size, sourcename, preserveComments, raiseerror); } void sq_dumpast(HSQUIRRELVM v, SQCompilation::SqASTData *astData, bool nodesLocation, OutputStream *s) { IndentedTreeRenderer rv(s); rv.printNodesLocation = nodesLocation; rv.render(astData->root); } void sq_dumpbytecode(HSQUIRRELVM v, HSQOBJECT obj, OutputStream *s, int instruction_index) { if (sq_isfunction(obj)) { Dump(s, _funcproto(obj), true, instruction_index); } else if (sq_isclosure(obj)) { SQFunctionProto *proto = _closure(obj)->_function; Dump(s, proto, true, instruction_index); } else { s->writeString("Object is not a FunctionProto"); } } void sq_reset_static_memos(HSQUIRRELVM v, HSQOBJECT func) { if (sq_isfunction(func)) { ResetStaticMemos(_funcproto(func), _ss(v)); } else if (sq_isclosure(func)) { ResetStaticMemos(_closure(func)->_function, _ss(v)); } } SQRESULT sq_translateasttobytecode(HSQUIRRELVM v, SQCompilation::SqASTData *astData, const HSQOBJECT *bindings, const char *s, SQInteger size, SQBool raiseerror) { SQObjectPtr o; if (TranslateASTToBytecode(v, astData, bindings, s, size, o, raiseerror)) { v->Push(SQObjectPtr(SQClosure::Create(_ss(v), _funcproto(o)))); return SQ_OK; } return SQ_ERROR; } SQRESULT sq_getimports(HSQUIRRELVM v, SQCompilation::SqASTData *astData, SQInteger *num, SQModuleImport **imports) { return AstGetImports(v, astData, num, imports) ? SQ_OK : SQ_ERROR; } void sq_freeimports(HSQUIRRELVM v, SQInteger num, SQModuleImport *imports) { AstFreeImports(v, num, imports); } void sq_analyzeast(HSQUIRRELVM v, SQCompilation::SqASTData *astData, const HSQOBJECT *bindings, const char *s, SQInteger size) { AnalyzeCode(v, astData, bindings, s, size); } void sq_checktrailingspaces(HSQUIRRELVM v, const char *sourceName, const char *s, SQInteger size) { StaticAnalyzer::checkTrailingWhitespaces(v, sourceName, s, size); } SQCompilation::SqASTData *sq_allocateASTData(HSQUIRRELVM v) { SqASTData *astData = (SqASTData *)sq_vm_malloc(_ss(v)->_alloc_ctx, sizeof(SqASTData)); void *arenaPtr = sq_vm_malloc(_ss(v)->_alloc_ctx, sizeof(Arena)); astData->astArena = new (arenaPtr) Arena(_ss(v)->_alloc_ctx, "AST"); void *commentsPtr = sq_vm_malloc(_ss(v)->_alloc_ctx, sizeof(Comments)); astData->comments = new (commentsPtr) Comments(_ss(v)->_alloc_ctx); astData->root = nullptr; astData->sourceName[0] = 0; return astData; } void sq_releaseASTData(HSQUIRRELVM v, SQCompilation::SqASTData *astData) { if (!astData) return; Comments *comments = astData->comments; if (comments) { comments->~Comments(); sq_vm_free(_ss(v)->_alloc_ctx, comments, sizeof(Comments)); } Arena *arena = astData->astArena; arena->~Arena(); sq_vm_free(_ss(v)->_alloc_ctx, arena, sizeof(Arena)); sq_vm_free(_ss(v)->_alloc_ctx, astData, sizeof(SQCompilation::SqASTData)); } void sq_move(HSQUIRRELVM dest,HSQUIRRELVM src,SQInteger idx) { dest->Push(stack_get(src,idx)); } static bool validate_freezable(HSQUIRRELVM v, SQObjectPtr &o) { SQObjectType tp = sq_type(o); if (tp != OT_ARRAY && tp != OT_TABLE && tp != OT_INSTANCE && tp != OT_CLASS && tp != OT_USERDATA) { v->Raise_Error("Cannot freeze %s", IdType2Name(tp)); return false; } return true; } SQRESULT sq_freeze(HSQUIRRELVM v, SQInteger idx) { SQObjectPtr &o = stack_get(v, idx); if (!validate_freezable(v, o)) return SQ_ERROR; SQObjectPtr dst = o; dst._flags |= SQOBJ_FLAG_IMMUTABLE; v->Push(dst); return SQ_OK; } SQUIRREL_API SQRESULT sq_freeze_inplace(HSQUIRRELVM v, SQInteger idx) { SQObjectPtr &o = stack_get(v, idx); if (!validate_freezable(v, o)) return SQ_ERROR; o._flags |= SQOBJ_FLAG_IMMUTABLE; return SQ_OK; } SQRESULT sq_mark_pure_inplace(HSQUIRRELVM v, SQInteger idx) { SQObjectPtr &o = stack_get(v, idx); SQObjectType tp = sq_type(o); if (tp == OT_CLOSURE) _closure(o)->_function->_purefunction = true; else if (tp == OT_NATIVECLOSURE) _nativeclosure(o)->_purefunction = true; else { v->Raise_Error("Type '%s' cannot be marked as a pure function", IdType2Name(tp)); return SQ_ERROR; } return SQ_OK; } bool sq_is_pure_function(HSQOBJECT *func) { if (!func) return false; SQObjectType tp = sq_type(*func); if (tp == OT_CLOSURE) return _closure(*func)->_function->_purefunction; else if (tp == OT_NATIVECLOSURE) return _nativeclosure(*func)->_purefunction; else return false; } void sq_setprintfunc(HSQUIRRELVM v, SQPRINTFUNCTION printfunc,SQPRINTFUNCTION errfunc) { _ss(v)->_printfunc = printfunc; _ss(v)->_errorfunc = errfunc; } SQPRINTFUNCTION sq_getprintfunc(HSQUIRRELVM v) { return _ss(v)->_printfunc; } SQPRINTFUNCTION sq_geterrorfunc(HSQUIRRELVM v) { return _ss(v)->_errorfunc; } SQAllocContext sq_getallocctx(HSQUIRRELVM v) { return _ss(v)->_alloc_ctx; } void *sq_malloc(SQAllocContext ctx, SQUnsignedInteger size) { return SQ_MALLOC(ctx, size); } void *sq_realloc(SQAllocContext ctx, void* p,SQUnsignedInteger oldsize,SQUnsignedInteger newsize) { return SQ_REALLOC(ctx,p,oldsize,newsize); } void sq_free(SQAllocContext ctx, void *p,SQUnsignedInteger size) { SQ_FREE(ctx,p,size); } SQRESULT sq_limitthreadaccess(HSQUIRRELVM vm, int64_t tid) { vm->check_thread_access = tid; return SQ_OK; } bool sq_canaccessfromthisthread(HSQUIRRELVM vm) { return vm->CanAccessFromThisThread(); } void sq_resetanalyzerconfig() { SQCompilation::resetAnalyzerConfig(); } bool sq_loadanalyzerconfig(const char *configFileName) { return SQCompilation::loadAnalyzerConfigFile(configFileName); } bool sq_loadanalyzerconfigblk(const KeyValueFile &config) { return SQCompilation::loadAnalyzerConfigFile(config); } bool sq_setdiagnosticstatebyname(const char *diagId, bool state) { return SQCompilationContext::enableWarning(diagId, state); } bool sq_setdiagnosticstatebyid(int32_t id, bool state) { return SQCompilationContext::enableWarning(id, state); } void sq_printwarningslist(FILE *ostream) { SQCompilationContext::printAllWarnings(ostream); } void sq_enablesyntaxwarnings(bool on) { SQCompilationContext::switchSyntaxWarningsState(on); } void sq_checkglobalnames(HSQUIRRELVM v) { StaticAnalyzer::reportGlobalNamesWarnings(v); } void sq_mergeglobalnames(const HSQOBJECT *bindings) { StaticAnalyzer::mergeKnownBindings(bindings); } ================================================ FILE: squirrel/sqarray.h ================================================ /* see copyright notice in squirrel.h */ #ifndef _SQARRAY_H_ #define _SQARRAY_H_ #include "vartrace.h" struct SQArray : public CHAINABLE_OBJ { private: SQArray(SQSharedState *ss,SQInteger nsize) : _values(ss->_alloc_ctx) VT_DECL_CTR(ss->_alloc_ctx) { _values.resize(nsize); VT_RESIZE(nsize); INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); } ~SQArray() { REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); } public: static SQArray* Create(SQSharedState *ss,SQInteger nInitialSize){ SQArray *newarray=(SQArray*)SQ_MALLOC(ss->_alloc_ctx, sizeof(SQArray)); new (newarray) SQArray(ss,nInitialSize); return newarray; } #ifndef NO_GARBAGE_COLLECTOR void Mark(SQCollectable **chain); SQObjectType GetType() {return OT_ARRAY;} #endif void Finalize(){ _values.resize(0); VT_RESIZE(0); } VT_CODE(VarTrace * GetVarTracePtr(const SQInteger nidx) { return &varTrace[nidx]; }) bool Get(const SQInteger nidx,SQObjectPtr &val) { if((SQUnsignedInteger)nidx<(SQUnsignedInteger)_values.size()){ SQObjectPtr &o = _values[nidx]; val = _realval(o); return true; } else return false; } bool Set(const SQInteger nidx,const SQObjectPtr &val) { if((SQUnsignedInteger)nidx<(SQUnsignedInteger)_values.size()){ _values[nidx]=val; VT_TRACE(nidx, val, _ss(this)->_root_vm); return true; } else return false; } SQInteger Next(const SQObjectPtr &refpos,SQObjectPtr &outkey,SQObjectPtr &outval) { SQUnsignedInteger idx=TranslateIndex(refpos); if (idx<_values.size()) { //first found outkey=(SQInteger)idx; SQObjectPtr &o = _values[idx]; outval = _realval(o); //return idx for the next iteration return ++idx; } //nothing to iterate anymore return -1; } SQArray *Clone(){SQArray *anew=Create(_opt_ss(this),0); anew->_values.copy(_values); VT_CLONE_FROM_TO(this, anew); return anew; } SQInteger Size() const {return _values.size();} void Resize(SQInteger size, SQBool shrink = SQTrue) { SQObjectPtr _null; Resize(size,_null, shrink); } void Resize(SQInteger size,SQObjectPtr &fill, SQBool shrink = SQTrue) { _values.resize(size,fill); VT_RESIZE(size); if (shrink) ShrinkIfNeeded(); } void Reserve(SQInteger size) { _values.reserve(size); VT_RESERVE(size); } void Append(const SQObject &o){_values.push_back(SQObjectPtr(o)); VT_PUSHBACK(o, _ss(this)->_root_vm); } void Extend(const SQArray *a); SQObjectPtr &Top(){return _values.top();} void Pop(){_values.pop_back(); VT_POPBACK(); ShrinkIfNeeded(); } bool Insert(SQInteger idx,const SQObject &val){ if(idx < 0 || idx > (SQInteger)_values.size()) return false; _values.insert(idx,SQObjectPtr(val)); VT_INSERT(idx, val, _ss(this)->_root_vm); return true; } void ShrinkIfNeeded() { if(_values.size() <= _values.capacity()>>2) //shrink the array _values.shrinktofit(); } bool Remove(SQInteger idx, bool shrink=true){ if(idx < 0 || idx >= (SQInteger)_values.size()) return false; _values.remove(idx); VT_REMOVE(idx); if (shrink) ShrinkIfNeeded(); return true; } void Release() { SQAllocContext ctx = _values._alloc_ctx; sq_delete(ctx, this, SQArray); } bool IsBinaryEqual(const SQArray *o); SQObjectPtrVec _values; VT_DECL_VEC; }; #endif //_SQARRAY_H_ ================================================ FILE: squirrel/sqbaselib.cpp ================================================ /* see copyright notice in squirrel.h */ #include "sqpcheader.h" #include "sqvm.h" #include "sqstring.h" #include "sqtable.h" #include "sqarray.h" #include "sqfuncproto.h" #include "sqclosure.h" #include "sqclass.h" #include #include #include #include "compiler/sqtypeparser.h" #include #ifndef SQ_BASELIB_INVOKE_CB_ERR_HANDLER #define SQ_BASELIB_INVOKE_CB_ERR_HANDLER SQTrue #endif static SQInteger delegable_getfuncinfos(HSQUIRRELVM v); static SQInteger class_getfuncinfos(HSQUIRRELVM v); SQInteger sq_deduplicate_object(HSQUIRRELVM vm, int index); #define SQ_CHECK_IMMUTABLE_SELF \ if (sq_objflags(stack_get(v,1)) & SQOBJ_FLAG_IMMUTABLE) \ return sq_throwerror(v, "Cannot modify immutable object"); #define SQ_CHECK_IMMUTABLE_OBJ(o) \ if (sq_objflags(o) & SQOBJ_FLAG_IMMUTABLE) \ return sq_throwerror(v, "Cannot modify immutable object"); static bool sq_parse_float(const char* str_begin, const char * str_end, SQObjectPtr& res, SQInteger base) { #if SQ_USE_STD_FROM_CHARS SQFloat r; auto ret = std::from_chars(str_begin, str_end, r); for (const char* ptr = ret.ptr; ptr != str_end; ++ptr) if (*ptr != '0') return false; if (ret.ec == std::errc::invalid_argument) return false; res = r; #else char* end; SQFloat r = SQFloat(strtod(str_begin, &end)); if (str_begin == end) return false; res = r; #endif return true; } static bool sq_parse_int(const char* str_begin, const char* str_end, SQObjectPtr& res, SQInteger base) { char* end; const char* e = str_begin; bool iseintbase = base > 13; //to fix error converting hexadecimals with e like 56f0791e char c; while ((c = *e) != '\0') { if (c == '.' || (!iseintbase && (c == 'E' || c == 'e'))) //e and E is for scientific notation return sq_parse_float(str_begin, str_end, res, base); e++; } SQInteger r = SQInteger(scstrtol(str_begin, &end, (int)base)); if (str_begin == end) return false; res = r; return true; } static SQInteger get_allowed_args_count(const SQObject &closure, SQInteger num_supported) { if(sq_type(closure) == OT_CLOSURE) { return _closure(closure)->_function->_nparameters; } else if (sq_type(closure) == OT_NATIVECLOSURE) { SQInteger nParamsCheck = _nativeclosure(closure)->_nparamscheck; if (nParamsCheck > 0) return nParamsCheck; else // push all params when there is no check or only minimal count set return num_supported; } assert(false); return -1; } static SQInteger base_getroottable(HSQUIRRELVM v) { v->Push(v->_roottable); return 1; } static SQInteger base_getconsttable(HSQUIRRELVM v) { v->Push(_ss(v)->_consts); return 1; } SQInteger __sq_getcallstackinfos(HSQUIRRELVM v,SQInteger level) { SQStackInfos si; SQInteger seq = 0; const char *name = NULL; if (SQ_SUCCEEDED(sq_stackinfos(v, level, &si))) { const char *fn = "unknown"; const char *src = "unknown"; if(si.funcname)fn = si.funcname; if(si.source)src = si.source; sq_newtable(v); sq_pushstring(v, "func", -1); sq_pushstring(v, fn, -1); sq_newslot(v, -3, SQFalse); sq_pushstring(v, "src", -1); sq_pushstring(v, src, -1); sq_newslot(v, -3, SQFalse); sq_pushstring(v, "line", -1); sq_pushinteger(v, si.line); sq_newslot(v, -3, SQFalse); sq_pushstring(v, "locals", -1); sq_newtable(v); seq=0; while ((name = sq_getlocal(v, level, seq))) { sq_pushstring(v, name, -1); sq_push(v, -2); sq_newslot(v, -4, SQFalse); sq_pop(v, 1); seq++; } sq_newslot(v, -3, SQFalse); return 1; } return 0; } static SQInteger base_assert(HSQUIRRELVM v) { if(SQVM::IsFalse(stack_get(v,2))){ if (sq_gettop(v) > 2) { SQObjectPtr &arg = stack_get(v,3); SQInteger msgIdx = 0; if (sq_type(arg)==OT_CLOSURE || sq_type(arg)==OT_NATIVECLOSURE) { sq_pushroottable(v); if (SQ_SUCCEEDED(sq_call(v, 1, SQTrue, SQFalse))) msgIdx = -1; } else msgIdx = 3; if (msgIdx!=0 && SQ_SUCCEEDED(sq_tostring(v,msgIdx))) { const char *str = 0; if (SQ_SUCCEEDED(sq_getstring(v,-1,&str))) return sq_throwerror(v, str); } } return sq_throwerror(v, "assertion failed"); } return 0; } static SQInteger get_slice_params(HSQUIRRELVM v,SQInteger &sidx,SQInteger &eidx,SQObjectPtr &o) { SQInteger top = sq_gettop(v); sidx=0; eidx=0; o=stack_get(v,1); if(top>1){ SQObjectPtr &start=stack_get(v,2); if(sq_type(start)!=OT_NULL && sq_isnumeric(start)){ sidx=tointeger(start); } } if(top>2){ SQObjectPtr &end=stack_get(v,3); if(sq_isnumeric(end)){ eidx=tointeger(end); } } else { eidx = sq_getsize(v,1); } return 1; } static SQInteger base_print(HSQUIRRELVM v, SQPRINTFUNCTION pf, bool newline) { if (!pf) return 0; const SQInteger top = sq_gettop(v); SQObjectPtr s; for (SQInteger i = 2; i <= top; ++i) { if (i > 2) pf(v, " "); if (v->ToString(stack_get(v, i), s)) pf(v, "%s", _stringval(s)); else pf(v, "< _tostring() call error >"); } if (newline) pf(v, "\n"); return 0; } static SQInteger base_print_newline(HSQUIRRELVM v) { return base_print(v, _ss(v)->_printfunc, true); } static SQInteger base_print_(HSQUIRRELVM v) { return base_print(v, _ss(v)->_printfunc, false); } static SQInteger base_error_newline(HSQUIRRELVM v) { return base_print(v, _ss(v)->_errorfunc, true); } static SQInteger base_error_(HSQUIRRELVM v) { return base_print(v, _ss(v)->_errorfunc, false); } static SQInteger base_compilestring(HSQUIRRELVM v) { SQInteger nargs=sq_gettop(v); const char *src=NULL,*name="unnamedbuffer"; SQInteger size; sq_getstringandsize(v,2,&src,&size); if (nargs>2) { sq_getstring(v,3,&name); } HSQOBJECT bindings; bool ownBindings = false; if (nargs>3) { sq_getstackobj(v, 4, &bindings); } else { ownBindings = true; sq_newtable(v); sq_getstackobj(v, -1, &bindings); sq_addref(v, &bindings); sq_poptop(v); } // overwrites existing keys in bindings with ones from base lib sq_pushobject(v, bindings); sq_registerbaselib(v); sq_poptop(v); bool ok = SQ_SUCCEEDED(sq_compile(v, src, size, name, SQFalse, &bindings)); if (ownBindings) sq_release(v, &bindings); return ok ? 1 : SQ_ERROR; } static SQInteger base_newthread(HSQUIRRELVM v) { SQObjectPtr &func = stack_get(v,2); SQInteger stksize = (_closure(func)->_function->_stacksize << 1) + 2; // -V629 HSQUIRRELVM newv = sq_newthread(v, (stksize < MIN_STACK_OVERHEAD + 2)? MIN_STACK_OVERHEAD + 2 : stksize); sq_move(newv,v,-2); return 1; } static SQInteger base_suspend(HSQUIRRELVM v) { return sq_suspendvm(v); } static SQInteger builtin_array_ctor(HSQUIRRELVM v) { SQInteger size = tointeger(stack_get(v,2)); if (size < 0) return sq_throwerror(v, "array size must be non-negative"); SQArray *a; if(sq_gettop(v) > 2) { a = SQArray::Create(_ss(v),0); a->Resize(size,stack_get(v,3)); } else { a = SQArray::Create(_ss(v),size); } v->Push(SQObjectPtr(a)); return 1; } static SQInteger base_type(HSQUIRRELVM v) { SQObjectPtr &o = stack_get(v,2); v->Push(SQObjectPtr(SQString::Create(_ss(v),GetTypeName(o),-1))); return 1; } static SQInteger base_callee(HSQUIRRELVM v) { if(v->_callsstacksize > 1) { v->Push(v->_callsstack[v->_callsstacksize - 2]._closure); return 1; } return sq_throwerror(v,"no closure in the calls stack"); } static SQInteger base_freeze(HSQUIRRELVM v) { return SQ_SUCCEEDED(sq_freeze(v, 2)) ? 1 : SQ_ERROR; } static SQInteger base_getobjflags(HSQUIRRELVM v) { sq_pushinteger(v, sq_objflags(stack_get(v, 2))); return 1; } static SQInteger base_deduplicate_object(HSQUIRRELVM v) { return sq_deduplicate_object(v, 2); } static SQInteger base_classof(HSQUIRRELVM v) { SQObjectPtr &obj = stack_get(v, 2); if (sq_type(obj) == OT_INSTANCE) { v->Push(SQObjectPtr(_instance(obj)->_class)); return 1; } else if (SQClass *cls = v->GetBuiltInClassForType(sq_type(obj))) { v->Push(SQObjectPtr(cls)); return 1; } assert(0); return 0; } static const SQRegFunctionFromStr base_funcs[] = { { base_getroottable, "getroottable(): table" }, { base_getconsttable, "getconsttable(): table" }, { base_assert, "assert(condition, [message: function|string])" }, { base_print_, "print(...)" }, { base_print_newline, "println(...)" }, { base_error_, "error(...)" }, { base_error_newline, "errorln(...)" }, { base_compilestring, "compilestring(source: string, [name: string, bindings: table|null]): function" }, { base_newthread, "newthread(func: function): thread" }, { base_suspend, "suspend(...): any" }, { builtin_array_ctor, "array(size: int, [default_value]): array" }, { base_type, "pure type(obj): string" }, { base_callee, "callee(): function" }, { base_freeze, "freeze(obj): any" }, { base_deduplicate_object, "deduplicate_object(obj: table|array|null): table|array|null" }, { base_getobjflags, "getobjflags(obj): int" }, { NULL, NULL } }; SQRESULT sq_registerbaselib(HSQUIRRELVM v) { SQInteger i=0; while(base_funcs[i].f) { sq_new_closure_slot_from_decl_string(v, base_funcs[i].f, 0, base_funcs[i].declstring, base_funcs[i].docstring); i++; } return SQ_OK; } static const SQRegFunctionFromStr types_funcs[] = { { base_classof, "pure classof(obj): class" }, { NULL, NULL } }; SQRESULT sq_registertypeslib(HSQUIRRELVM v) { SQInteger i=0; while(types_funcs[i].f) { sq_new_closure_slot_from_decl_string(v, types_funcs[i].f, 0, types_funcs[i].declstring, types_funcs[i].docstring); i++; } #define REG_CLS(name, cls) \ sq_pushstring(v, #name, -1); \ v->Push(cls); \ sq_newslot(v, -3, SQFalse); REG_CLS(Integer, _ss(v)->_integer_class) REG_CLS(Float, _ss(v)->_float_class); REG_CLS(Bool, _ss(v)->_bool_class); REG_CLS(String, _ss(v)->_string_class); REG_CLS(Array, _ss(v)->_array_class); REG_CLS(Table, _ss(v)->_table_class); REG_CLS(Function, _ss(v)->_function_class); REG_CLS(Generator, _ss(v)->_generator_class); REG_CLS(Thread, _ss(v)->_thread_class); REG_CLS(Class, _ss(v)->_class_class); REG_CLS(Instance, _ss(v)->_instance_class); REG_CLS(WeakRef, _ss(v)->_weakref_class); REG_CLS(UserData, _ss(v)->_userdata_class); REG_CLS(Null, _ss(v)->_null_class); #undef REG_CLS return SQ_OK; } void sq_base_register(HSQUIRRELVM v) { sq_pushroottable(v); sq_registerbaselib(v); sq_pop(v,1); sq_pushconsttable(v); sq_pushstring(v, "SQOBJ_FLAG_IMMUTABLE", -1); sq_pushinteger(v, SQOBJ_FLAG_IMMUTABLE); sq_newslot(v, -3, SQFalse); sq_pop(v,1); } static SQInteger default_type_method_len(HSQUIRRELVM v) { v->Push(SQObjectPtr(SQInteger(sq_getsize(v,1)))); return 1; } static SQInteger default_type_method_tofloat(HSQUIRRELVM v) { SQObjectPtr &o=stack_get(v,1); switch(sq_type(o)){ case OT_STRING:{ SQObjectPtr res; if(sq_parse_float(_stringval(o), _stringval(o) + _string(o)->_len, res, 10)){ v->Push(SQObjectPtr(tofloat(res))); break; }} return sq_throwerror(v, "cannot convert the string to float"); case OT_INTEGER:case OT_FLOAT: v->Push(SQObjectPtr(tofloat(o))); break; case OT_BOOL: v->Push(SQObjectPtr((SQFloat)(_integer(o)?1:0))); break; default: v->PushNull(); break; } return 1; } static SQInteger default_type_method_tointeger(HSQUIRRELVM v) { SQObjectPtr &o=stack_get(v,1); SQInteger base = 10; if(sq_gettop(v) > 1) { sq_getinteger(v,2,&base); } switch(sq_type(o)){ case OT_STRING:{ SQObjectPtr res; if(sq_parse_int(_stringval(o), _stringval(o) + _string(o)->_len, res, base)){ v->Push(SQObjectPtr(tointeger(res))); break; }} return sq_throwerror(v, "cannot convert the string to integer"); break; case OT_INTEGER:case OT_FLOAT: v->Push(SQObjectPtr(tointeger(o))); break; case OT_BOOL: v->Push(SQObjectPtr(_integer(o)?(SQInteger)1:(SQInteger)0)); break; default: v->PushNull(); break; } return 1; } static SQInteger default_type_method_tostring(HSQUIRRELVM v) { if(SQ_FAILED(sq_tostring(v,1))) return SQ_ERROR; return 1; } static SQInteger obj_type_method_weakref(HSQUIRRELVM v) { sq_weakref(v,1); return 1; } static SQInteger obj_clear(HSQUIRRELVM v) { SQ_CHECK_IMMUTABLE_SELF; return SQ_SUCCEEDED(sq_clear(v,-1)) ? 1 : SQ_ERROR; } static SQInteger number_type_method_tochar(HSQUIRRELVM v) { SQObject &o=stack_get(v,1); char c = (char)tointeger(o); v->Push(SQObjectPtr(SQString::Create(_ss(v),(const char *)&c,1))); return 1; } static SQInteger container_update(HSQUIRRELVM v) { SQ_CHECK_IMMUTABLE_SELF; SQInteger top = sq_gettop(v); for (int arg=2; arg<=top; ++arg) { SQObjectType argType = sq_gettype(v, arg); if (!(argType & (_RT_TABLE | _RT_CLASS | _RT_INSTANCE))) { v->Raise_ParamTypeError(arg, _RT_TABLE | _RT_CLASS | _RT_INSTANCE, argType); return SQ_ERROR; } sq_pushnull(v); while (SQ_SUCCEEDED(sq_next(v, arg))) { sq_newslot(v, 1, false); } sq_pop(v, 1); // pops the iterator } sq_push(v, 1); return 1; } static SQInteger container_merge(HSQUIRRELVM v) { SQObject &o = stack_get(v,1); SQInteger top = sq_gettop(v); switch (sq_type(o)) { case OT_TABLE: sq_newtableex(v, sq_getsize(v, 1)); break; case OT_CLASS: sq_newclass(v, SQFalse); break; default: return sq_throwerror(v, "merge() only works on tables and classes"); } for (int arg=1; arg<=top; ++arg) { SQObjectType argType = sq_gettype(v, arg); if (!(argType & (_RT_TABLE | _RT_CLASS | _RT_INSTANCE))) { v->Raise_ParamTypeError(arg, _RT_TABLE | _RT_CLASS | _RT_INSTANCE, argType); return SQ_ERROR; } sq_pushnull(v); while (SQ_SUCCEEDED(sq_next(v, arg))) { sq_newslot(v, top+1, false); } sq_pop(v, 1); // pops the iterator } return 1; } static SQInteger container_each(HSQUIRRELVM v) { const SQObjectPtr &o = stack_get(v,1); const SQObjectPtr &closure = stack_get(v, 2); SQInteger nArgs = get_allowed_args_count(closure, 4); SQObjectPtr tmp; sq_pushnull(v); while (SQ_SUCCEEDED(sq_next(v, 1))) { SQInteger iterTop = sq_gettop(v); v->PushNull(); v->Push(stack_get(v, iterTop)); if (nArgs >= 3) v->Push(stack_get(v, iterTop-1)); if (nArgs >= 4) v->Push(o); bool callRes = v->Call(closure, nArgs, v->_top-nArgs, tmp, SQ_BASELIB_INVOKE_CB_ERR_HANDLER); v->Pop(2+nArgs); if (!callRes) return SQ_ERROR; } sq_pop(v, 1); // pops the iterator return 0; } static SQInteger container_findindex(HSQUIRRELVM v) { const SQObjectPtr &o = stack_get(v,1); const SQObjectPtr &closure = stack_get(v, 2); SQInteger nArgs = get_allowed_args_count(closure, 4); SQObjectPtr tmp; sq_pushnull(v); while (SQ_SUCCEEDED(sq_next(v, 1))) { SQInteger iterTop = sq_gettop(v); v->PushNull(); v->Push(stack_get(v, iterTop)); if (nArgs >= 3) v->Push(stack_get(v, iterTop-1)); if (nArgs >= 4) v->Push(o); bool callRes = v->Call(closure, nArgs, v->_top-nArgs, tmp, SQ_BASELIB_INVOKE_CB_ERR_HANDLER); if (!callRes) return SQ_ERROR; if (!v->IsFalse(tmp)) { v->Push(stack_get(v, iterTop-1)); return 1; } v->Pop(2+nArgs); } sq_pop(v, 1); // pops the iterator return 0; } static SQInteger container_findvalue(HSQUIRRELVM v) { if (sq_gettop(v) > 3) return sq_throwerror(v, "Too many arguments for findvalue()"); const SQObjectPtr &o = stack_get(v,1); const SQObjectPtr &closure = stack_get(v, 2); SQInteger nArgs = get_allowed_args_count(closure, 4); SQObjectPtr tmp; sq_pushnull(v); while (SQ_SUCCEEDED(sq_next(v, 1))) { SQInteger iterTop = sq_gettop(v); v->PushNull(); v->Push(stack_get(v, iterTop)); if (nArgs >= 3) v->Push(stack_get(v, iterTop-1)); if (nArgs >= 4) v->Push(o); bool callRes = v->Call(closure, nArgs, v->_top-nArgs, tmp, SQ_BASELIB_INVOKE_CB_ERR_HANDLER); if (!callRes) return SQ_ERROR; if (!v->IsFalse(tmp)) { v->Push(stack_get(v, iterTop)); return 1; } v->Pop(2+nArgs); } sq_pop(v, 1); // pops the iterator return sq_gettop(v) > 2 ? 1 : 0; } static SQInteger container_hasindex(HSQUIRRELVM v) { const SQObjectPtr &self = stack_get(v, 1); SQObjectType t = sq_type(self); if (t != OT_TABLE && t != OT_CLASS && t != OT_INSTANCE) return sq_throwerror(v, "hasindex not supported for this type"); const SQObjectPtr &key = stack_get(v, 2); v->Push(self); v->Push(key); bool exists = SQ_SUCCEEDED(sq_rawget(v, -2)); sq_pushbool(v, exists); return 1; } static SQInteger container_hasvalue(HSQUIRRELVM v) { const SQObject &o = stack_get(v, 1); SQObjectType t = sq_type(o); if (t != OT_TABLE) return sq_throwerror(v, "hasvalue not supported for this type"); const SQObjectPtr &value = stack_get(v, 2); bool found = false; SQTable *tbl = _table(o); SQObjectPtr itr, key, val; SQInteger nitr; while ((nitr = tbl->Next(false, itr, key, val)) != -1) { itr = (SQInteger)nitr; if (SQVM::IsEqual(val, value)) { found = true; break; } } sq_pushbool(v, found); return 1; } static SQInteger table_rawdelete(HSQUIRRELVM v) { SQ_CHECK_IMMUTABLE_SELF; if(SQ_FAILED(sq_rawdeleteslot(v,1,SQTrue))) return SQ_ERROR; return 1; } static SQInteger container_rawexists(HSQUIRRELVM v) { if(SQ_SUCCEEDED(sq_rawget(v,-2))) { sq_pushbool(v,SQTrue); return 1; } sq_pushbool(v,SQFalse); return 1; } static SQInteger container_rawset(HSQUIRRELVM v) { SQ_CHECK_IMMUTABLE_SELF; return SQ_SUCCEEDED(sq_rawset(v,-3)) ? 1 : SQ_ERROR; } static SQInteger container_rawget(HSQUIRRELVM v) { // container type check is done by the typemask if (SQ_FAILED(sq_rawget(v, 1))) return sq_throwerror(v,"the index doesn't exist"); return 1; } static SQInteger table_filter(HSQUIRRELVM v) { const SQObjectPtr &o = stack_get(v,1); SQTable *tbl = _table(o); const SQObjectPtr &closure = stack_get(v, 2); SQInteger nArgs = get_allowed_args_count(closure, 4); SQObjectPtr ret(SQTable::Create(_ss(v),0)); SQObjectPtr temp; SQObjectPtr itr, key, val; SQInteger nitr; while((nitr = tbl->Next(false, itr, key, val)) != -1) { itr = (SQInteger)nitr; v->PushNull(); v->Push(val); if (nArgs >= 3) v->Push(key); if (nArgs >= 4) v->Push(o); bool callRes = v->Call(closure, nArgs, v->_top - nArgs, temp, SQ_BASELIB_INVOKE_CB_ERR_HANDLER); v->Pop(nArgs); if (!callRes) return SQ_ERROR; if (!SQVM::IsFalse(temp)) { _table(ret)->NewSlot(key, val); } } v->Push(ret); return 1; } #define TABLE_TO_ARRAY_FUNC(_funcname_,_valname_) static SQInteger _funcname_(HSQUIRRELVM v) \ { \ SQObject &o = stack_get(v, 1); \ SQTable *t = _table(o); \ SQObjectPtr itr, key, val; \ SQInteger nitr, n = 0; \ SQInteger nitems = t->CountUsed(); \ SQArray *a = SQArray::Create(_ss(v), nitems); \ if (nitems) { \ while ((nitr = t->Next(false, itr, key, val)) != -1) { \ itr = (SQInteger)nitr; \ a->Set(n, _valname_); \ n++; \ } \ } \ v->Push(SQObjectPtr(a)); \ return 1; \ } TABLE_TO_ARRAY_FUNC(table_keys, key) TABLE_TO_ARRAY_FUNC(table_values, val) static SQInteger table_to_pairs(HSQUIRRELVM v) { SQObject &o = stack_get(v, 1); SQTable *t = _table(o); SQObjectPtr itr, key, val; SQInteger nitr, n = 0; SQInteger nitems = t->CountUsed(); SQArray *result = SQArray::Create(_ss(v), nitems); if (nitems) { while ((nitr = t->Next(false, itr, key, val)) != -1) { itr = (SQInteger)nitr; SQArray *item = SQArray::Create(_ss(v), 2); item->Set(0, key); item->Set(1, val); result->Set(n, SQObjectPtr(item)); n++; } } v->Push(SQObjectPtr(result)); return 1; } static SQInteger swap(HSQUIRRELVM v) { SQ_CHECK_IMMUTABLE_SELF; SQObjectPtr &o = stack_get(v, 1); SQObjectPtr &key1 = stack_get(v, 2); SQObjectPtr &key2 = stack_get(v, 3); switch(sq_type(o)) { case OT_ARRAY: { SQArray *arr = _array(o); if (!sq_isnumeric(key1) || !sq_isnumeric(key2)) return sq_throwerror(v,"invalid index type for an array"); const int asize = arr->Size(); int k1 = tointeger(key1); k1 = k1 >= 0 ? k1 : asize + k1; int k2 = tointeger(key2); k2 = k2 >= 0 ? k2 : asize + k2 ; if( k1 >= asize || k2 >= asize || k1 < 0 || k2 < 0) return sq_throwerror(v,"index is out of range"); _Swap(_array(o)->_values[k1], _array(o)->_values[k2]); break; } case OT_TABLE: { SQObjectPtr val1, val2; if (!_table(o)->Get(key1, val1) || !_table(o)->Get(key2, val2)) sq_throwerror(v,"the index doesn't exist"); _table(o)->Set(key1, val2); _table(o)->Set(key2, val1); break; } case OT_INSTANCE: { SQObjectPtr val1, val2; if (!_instance(o)->Get(key1, val1) || !_instance(o)->Get(key2, val2)) return sq_throwerror(v,"the index doesn't exist"); _instance(o)->Set(key1, val2); _instance(o)->Set(key2, val1); break; } default: return sq_throwerror(v,"swap works only on array/table/instance"); } v->Push(o); return 1; } static SQInteger array_to_table(HSQUIRRELVM v) { SQObject &o = stack_get(v, 1); SQArray *arr = _array(o); SQInteger count = arr->Size(); SQTable *result = SQTable::Create(_ss(v), count); if (count == 0) { v->Push(SQObjectPtr(result)); return 1; } SQObjectPtr item; bool expectArrays = arr->Get(0, item) && sq_isarray(item); for (SQInteger i = 0; i < count; i++) { SQObjectPtr key; SQObjectPtr val; if (!arr->Get(i, item)) return sq_throwerror(v, "totable() failed to get element from array"); bool isSimpleType = sq_isstring(item) || sq_isnumeric(item) || sq_isbool(item) || sq_isnull(item); if (expectArrays && sq_isarray(item) && _array(item)->Get(0, key) && _array(item)->Get(1, val) && _array(item)->Size() == 2) result->NewSlot(key, val); else if (!expectArrays && isSimpleType) result->NewSlot(item, item); else { if (expectArrays && sq_isarray(item) && _array(item)->Size() != 2) return sq_throwerror(v, "totable() expected array of pairs [[key, value], ...], size of the each pair array must be exactly 2 elements"); else return sq_throwerror(v, "totable() expected array of pairs [[key, value], ...] or array of simple types [\"key1\", \"key2\", ...]"); } } v->Push(SQObjectPtr(result)); return 1; } static SQInteger __map_table(SQTable *dest, SQTable *src, HSQUIRRELVM v) { const SQObjectPtr &closure = stack_get(v, 2); SQInteger nArgs = get_allowed_args_count(closure, 4); SQObjectPtr itr, key, val; SQInteger nitr; SQObjectPtr temp; SQObjectPtr srcObj(src); while ((nitr = src->Next(false, itr, key, val)) != -1) { itr = (SQInteger)nitr; v->PushNull(); v->Push(val); if (nArgs >= 3) v->Push(key); if (nArgs >= 4) v->Push(srcObj); bool callRes = v->Call(closure, nArgs, v->_top - nArgs, temp, SQ_BASELIB_INVOKE_CB_ERR_HANDLER); v->Pop(nArgs); if (!callRes) { if (sq_isnull(v->_lasterror)) continue; else return SQ_ERROR; } dest->NewSlot(key, temp); } return 0; } static SQInteger table_map(HSQUIRRELVM v) { SQObject &o = stack_get(v, 1); SQObjectPtr ret(SQTable::Create(_ss(v), _table(o)->CountUsed())); if(SQ_FAILED(__map_table(_table(ret), _table(o), v))) return SQ_ERROR; v->Push(ret); return 1; } static SQInteger table_reduce(HSQUIRRELVM v) { const SQObjectPtr &o = stack_get(v,1); SQTable *tbl = _table(o); const SQObjectPtr &closure = stack_get(v, 2); bool gotAccum = false; SQObjectPtr accum; SQInteger nArgs = get_allowed_args_count(closure, 5); if (sq_gettop(v) > 2) { accum = stack_get(v, 3); gotAccum = true; } SQObjectPtr itr, key, val; SQInteger nitr; while ((nitr = tbl->Next(false, itr, key, val)) != -1) { itr = (SQInteger)nitr; if (!gotAccum) { accum = val; gotAccum = true; } else { v->PushNull(); v->Push(accum); v->Push(val); if (nArgs >= 4) v->Push(key); if (nArgs >= 5) v->Push(o); bool callRes = v->Call(closure, nArgs, v->_top - nArgs, accum, SQ_BASELIB_INVOKE_CB_ERR_HANDLER); v->Pop(nArgs); if (!callRes) return SQ_ERROR; } } if (!gotAccum) return 0; v->Push(accum); return 1; } static SQInteger table_replace_with(HSQUIRRELVM v) { SQ_CHECK_IMMUTABLE_SELF; SQTable *dst = _table(stack_get(v, 1)); dst->Clear(false); // Alternatively, consider copying hash nodes directly instead of iterating slots sq_pushnull(v); while (SQ_SUCCEEDED(sq_next(v, 2))) sq_newslot(v, 1, false); sq_pop(v, 1); // pop iterator v->Pop(1); // pop src, leave dst on stack as the return value return 1; } static SQInteger obj_clone(HSQUIRRELVM vm) { SQObjectPtr self = vm->PopGet(); SQObjectPtr target; if (!vm->Clone(self, target)) { return SQ_ERROR; } vm->Push(target); return 1; } static SQInteger obj_is_frozen(HSQUIRRELVM vm) { SQObjectPtr self = vm->PopGet(); SQObjectPtr target; sq_pushbool(vm, (self._flags & SQOBJ_FLAG_IMMUTABLE) != 0); return 1; } static SQInteger builtin_null_ctor(HSQUIRRELVM v); static SQInteger builtin_integer_ctor(HSQUIRRELVM v); static SQInteger builtin_float_ctor(HSQUIRRELVM v); static SQInteger builtin_bool_ctor(HSQUIRRELVM v); static SQInteger builtin_string_ctor(HSQUIRRELVM v); static SQInteger builtin_table_ctor(HSQUIRRELVM v); static SQInteger builtin_weakref_ctor(HSQUIRRELVM v); const SQRegFunction SQSharedState::_table_default_type_methods_funcz[]={ {"constructor",builtin_table_ctor,1, NULL}, {"len",default_type_method_len,1, "t", NULL, true}, {"rawget",container_rawget,2, "t", NULL, true}, {"rawset",container_rawset,3, "t"}, {"rawdelete",table_rawdelete,2, "t"}, {"rawin",container_rawexists,2, "t", NULL, true}, {"weakref",obj_type_method_weakref,1, NULL }, {"tostring",default_type_method_tostring,1, "."}, {"clear",obj_clear,1, "."}, {"map",table_map,2, "tc"}, {"filter",table_filter,2, "tc"}, {"reduce",table_reduce, -2, "tc"}, {"getfuncinfos",delegable_getfuncinfos,1, "t"}, {"each",container_each,2, "tc"}, {"findindex",container_findindex,2, "tc", NULL, true}, {"findvalue",container_findvalue,-2, "tc.", NULL, true}, {"keys",table_keys,1, "t", NULL, true}, {"values",table_values,1, "t", NULL, true}, {"__update",container_update, -2, "t|yt|y|x", NULL, false}, {"__merge",container_merge, -2, "t|yt|y|x", NULL, true}, {"topairs",table_to_pairs, 1, "t" }, {"replace_with",table_replace_with, 2, "tt" }, {"hasindex",container_hasindex,2, "t.", NULL, true}, {"hasvalue",container_hasvalue,2, "t.", NULL, true}, {"clone",obj_clone, 1, "." }, {"is_frozen",obj_is_frozen, 1, "." }, {"swap",swap, 3, "t.." }, {NULL,(SQFUNCTION)0,0,NULL} }; // Array type methods static SQInteger array_append(HSQUIRRELVM v) { SQ_CHECK_IMMUTABLE_SELF; SQArray *arr = _array(stack_get(v, 1)); SQInteger nitems = sq_gettop(v)-1; SQInteger offs = arr->Size(); arr->Resize(offs + nitems); for (SQInteger i=0; iSet(offs+i, stack_get(v, 2+i)); v->Push(stack_get(v, 1)); return 1; } static SQInteger array_extend(HSQUIRRELVM v) { SQ_CHECK_IMMUTABLE_SELF; SQArray *arr = _array(stack_get(v, 1)); SQInteger n = sq_gettop(v)-1; for (SQInteger i=0; iExtend(_array(stack_get(v,2+i))); } sq_pop(v,n); return 1; } static SQInteger array_reverse(HSQUIRRELVM v) { SQ_CHECK_IMMUTABLE_SELF; return SQ_SUCCEEDED(sq_arrayreverse(v,-1)) ? 1 : SQ_ERROR; } static SQInteger array_pop(HSQUIRRELVM v) { SQ_CHECK_IMMUTABLE_SELF; return SQ_SUCCEEDED(sq_arraypop(v,1,SQTrue))?1:SQ_ERROR; } static SQInteger array_top(HSQUIRRELVM v) { const SQObject &o=stack_get(v,1); if(_array(o)->Size()>0){ v->Push(_array(o)->Top()); return 1; } else return sq_throwerror(v,"top() on a empty array"); } static SQInteger array_insert(HSQUIRRELVM v) { const SQObject &o=stack_get(v,1); SQ_CHECK_IMMUTABLE_OBJ(o); SQObject &idx=stack_get(v,2); SQObject &val=stack_get(v,3); if(!_array(o)->Insert(tointeger(idx),val)) return sq_throwerror(v,"index out of range"); sq_pop(v,2); return 1; } static SQInteger array_remove(HSQUIRRELVM v) { const SQObject &o = stack_get(v, 1); SQ_CHECK_IMMUTABLE_OBJ(o); SQObject &idx = stack_get(v, 2); if(!sq_isnumeric(idx)) return sq_throwerror(v, "wrong type"); SQObjectPtr val; if(_array(o)->Get(tointeger(idx), val)) { _array(o)->Remove(tointeger(idx)); v->Push(val); return 1; } return sq_throwerror(v, "idx out of range"); } static SQInteger array_resize(HSQUIRRELVM v) { SQObject &o = stack_get(v, 1); SQ_CHECK_IMMUTABLE_OBJ(o); SQObject &nsize = stack_get(v, 2); SQObjectPtr fill; if(sq_isnumeric(nsize)) { SQInteger sz = tointeger(nsize); if (sz<0) return sq_throwerror(v, "resizing to negative length"); if(sq_gettop(v) > 2) fill = stack_get(v, 3); _array(o)->Resize(sz,fill); sq_settop(v, 1); return 1; } return sq_throwerror(v, "size must be a number"); } static SQInteger __map_array(SQArray *dest,SQArray *src,HSQUIRRELVM v, bool append) { SQObjectPtr temp; SQObjectPtr srcObj(src); SQInteger size = src->Size(); const SQObjectPtr &closure = stack_get(v, 2); SQInteger nArgs = get_allowed_args_count(closure, 4); for(SQInteger n = 0; n < size; n++) { src->Get(n,temp); v->PushNull(); v->Push(temp); if (nArgs >= 3) v->Push(SQObjectPtr(n)); if (nArgs >= 4) v->Push(srcObj); bool callRes = v->Call(closure, nArgs, v->_top - nArgs, temp, SQ_BASELIB_INVOKE_CB_ERR_HANDLER); v->Pop(nArgs); if (!callRes) { if (append && sq_isnull(v->_lasterror)) { continue; } return SQ_ERROR; } if (append) dest->Append(temp); else dest->Set(n, temp); } return 0; } static SQInteger array_map(HSQUIRRELVM v) { const SQObjectPtr &o = stack_get(v,1); SQInteger size = _array(o)->Size(); SQArray *dest = SQArray::Create(_ss(v),0); dest->Reserve(size); SQObjectPtr ret(dest); if(SQ_FAILED(__map_array(dest,_array(o),v,true))) return SQ_ERROR; dest->ShrinkIfNeeded(); v->Push(ret); return 1; } static SQInteger array_apply(HSQUIRRELVM v) { SQObject &o = stack_get(v,1); SQ_CHECK_IMMUTABLE_OBJ(o); if(SQ_FAILED(__map_array(_array(o),_array(o),v,false))) return SQ_ERROR; sq_pop(v,1); return 1; } static SQInteger array_reduce(HSQUIRRELVM v) { const SQObjectPtr &o = stack_get(v,1); SQArray *a = _array(o); SQInteger size = a->Size(); SQObjectPtr accum; SQInteger iterStart; if (sq_gettop(v)>2) { accum = stack_get(v,3); iterStart = 0; } else if (size==0) { return 0; } else { a->Get(0, accum); iterStart = 1; } const SQObjectPtr &closure = stack_get(v, 2); SQInteger nArgs = get_allowed_args_count(closure, 5); if (size > iterStart) { SQObjectPtr other; for (SQInteger n = iterStart; n < size; n++) { a->Get(n,other); v->PushNull(); v->Push(accum); v->Push(other); if (nArgs >= 4) v->Push(SQObjectPtr(n)); if (nArgs >= 5) v->Push(o); bool callRes = v->Call(closure, nArgs, v->_top - nArgs, accum, SQ_BASELIB_INVOKE_CB_ERR_HANDLER); v->Pop(nArgs); if (!callRes) return SQ_ERROR; } } v->Push(accum); return 1; } static SQInteger array_filter(HSQUIRRELVM v) { const SQObjectPtr &o = stack_get(v,1); SQArray *a = _array(o); const SQObjectPtr &closure = stack_get(v, 2); SQInteger nArgs = get_allowed_args_count(closure, 4); SQObjectPtr ret(SQArray::Create(_ss(v),0)); SQInteger size = a->Size(); SQObjectPtr val, temp; for(SQInteger n = 0; n < size; n++) { a->Get(n,val); v->PushNull(); v->Push(val); if (nArgs >= 3) v->Push(SQObjectPtr(n)); if (nArgs >= 4) v->Push(o); bool callRes = v->Call(closure, nArgs, v->_top - nArgs, temp, SQ_BASELIB_INVOKE_CB_ERR_HANDLER); v->Pop(nArgs); if (!callRes) return SQ_ERROR; if(!SQVM::IsFalse(temp)) { _array(ret)->Append(val); } } v->Push(ret); return 1; } static SQInteger _push_scan_index(HSQUIRRELVM v, SQInteger index) { if (index >= 0) { sq_pushinteger(v, index); return 1; } else return 0; } static SQInteger _push_scan_found_flag(HSQUIRRELVM v, SQInteger index) { sq_pushbool(v, index>=0); return 1; } static SQInteger _array_scan_for_value(HSQUIRRELVM v, SQInteger (*push_result)(HSQUIRRELVM v, SQInteger index)) { SQObject &o = stack_get(v,1); SQObjectPtr &val = stack_get(v,2); SQArray *a = _array(o); SQInteger size = a->Size(); SQObjectPtr temp; for(SQInteger n = 0; n < size; n++) { a->Get(n,temp); if(SQVM::IsEqual(temp,val)) return push_result(v, n); } return push_result(v, -1); } static SQInteger array_indexof(HSQUIRRELVM v) { return _array_scan_for_value(v, _push_scan_index); } static SQInteger array_contains(HSQUIRRELVM v) { return _array_scan_for_value(v, _push_scan_found_flag); } static bool _sort_compare(HSQUIRRELVM v, SQArray *arr, SQObjectPtr &a, SQObjectPtr &b, const SQObjectPtr &func, SQInteger &ret) { if (sq_isnull(func)) { if (!v->ObjCmp(a,b,ret)) return false; } else { SQInteger top = sq_gettop(v); v->PushNull(); v->Push(a); v->Push(b); SQObjectPtr *valptr = arr->_values._vals; SQUnsignedInteger precallsize = arr->_values.size(); SQObjectPtr out; bool callSucceeded = v->Call(func, 3, v->_top-3, out, false); if (!callSucceeded) { if (!sq_isstring( v->_lasterror)) v->Raise_Error("compare func failed"); return false; } if (!sq_isnumeric(out)) { v->Raise_Error("numeric value expected as return value of the compare function"); return false; } if (precallsize != arr->_values.size() || valptr != arr->_values._vals) { v->Raise_Error("array resized during sort operation"); return false; } ret = tointeger(out); sq_settop(v, top); return true; } return true; } static bool _hsort_sift_down(HSQUIRRELVM v,SQArray *arr, SQInteger root, SQInteger bottom, const SQObjectPtr &func) { SQInteger maxChild; SQInteger done = 0; SQInteger ret; SQInteger root2; while (((root2 = root * 2) <= bottom) && (!done)) { if (root2 == bottom) { maxChild = root2; } else { if(!_sort_compare(v,arr,arr->_values[root2],arr->_values[root2 + 1],func,ret)) return false; if (ret > 0) { maxChild = root2; } else { maxChild = root2 + 1; } } if(!_sort_compare(v,arr,arr->_values[root],arr->_values[maxChild],func,ret)) return false; if (ret < 0) { if (root == maxChild) { v->Raise_Error("inconsistent compare function"); return false; // We'd be swapping ourselve. The compare function is incorrect } _Swap(arr->_values[root],arr->_values[maxChild]); root = maxChild; } else { done = 1; } } return true; } static bool _hsort(HSQUIRRELVM v,SQObjectPtr &arr, SQInteger SQ_UNUSED_ARG(l), SQInteger SQ_UNUSED_ARG(r),const SQObjectPtr &func) { SQArray *a = _array(arr); SQInteger i; SQInteger array_size = a->Size(); for (i = (array_size / 2); i >= 0; i--) { if(!_hsort_sift_down(v,a, i, array_size - 1,func)) return false; } for (i = array_size-1; i >= 1; i--) { _Swap(a->_values[0],a->_values[i]); if(!_hsort_sift_down(v,a, 0, i-1,func)) return false; } return true; } static SQInteger array_sort(HSQUIRRELVM v) { SQObjectPtr func; SQObjectPtr &o = stack_get(v,1); SQ_CHECK_IMMUTABLE_OBJ(o); if (_array(o)->Size() > 1) { if(sq_gettop(v) == 2) func = stack_get(v, 2); if(!_hsort(v, o, 0, _array(o)->Size()-1, func)) return SQ_ERROR; } sq_settop(v,1); return 1; } static SQInteger clamp_int(SQInteger v, SQInteger minv, SQInteger maxv) { return (v < minv) ? minv : (v > maxv) ? maxv : v; } static SQInteger array_slice(HSQUIRRELVM v) { SQInteger sidx,eidx; SQObjectPtr o; if(get_slice_params(v,sidx,eidx,o)==-1)return -1; SQInteger alen = _array(o)->Size(); sidx = clamp_int((sidx < 0) ? (alen + sidx) : sidx, 0, alen); eidx = clamp_int((eidx < 0) ? (alen + eidx) : eidx, 0, alen); if (alen == 0 || eidx <= sidx) { SQArray *arr = SQArray::Create(_ss(v), 0); v->Push(SQObjectPtr(arr)); return 1; } SQArray *arr=SQArray::Create(_ss(v),eidx-sidx); SQObjectPtr t; SQInteger count=0; for(SQInteger i=sidx;iGet(i,t); arr->Set(count++,t); } v->Push(SQObjectPtr(arr)); return 1; } static SQInteger array_hasindex(HSQUIRRELVM v) { const SQObjectPtr &o = stack_get(v, 1); SQInteger idx = tointeger(stack_get(v, 2)); sq_pushbool(v, idx>=0 && idx < _array(o)->Size()); return 1; } static SQInteger array_replace_with(HSQUIRRELVM v) { SQ_CHECK_IMMUTABLE_SELF; SQArray *dst = _array(stack_get(v, 1)); SQArray *src = _array(stack_get(v, 2)); dst->_values.copy(src->_values); dst->ShrinkIfNeeded(); VT_CLONE_FROM_TO(src, dst); v->Pop(1); return 1; } const SQRegFunction SQSharedState::_array_default_type_methods_funcz[]={ {"constructor",builtin_array_ctor,-2, ".n."}, {"len",default_type_method_len,1, "a", NULL, true}, {"append",array_append,-2, "a."}, {"extend",array_extend,-2, "aa"}, {"pop",array_pop,1, "a"}, {"top",array_top,1, "a"}, {"insert",array_insert,3, "an."}, {"remove",array_remove,2, "an"}, {"resize",array_resize,-2, "an"}, {"reverse",array_reverse,1, "a"}, {"sort",array_sort,-1, "ac"}, {"slice",array_slice,-1, "ann"}, {"weakref",obj_type_method_weakref,1, NULL }, {"tostring",default_type_method_tostring,1, "."}, {"clear",obj_clear,1, "."}, {"map",array_map,2, "ac"}, {"apply",array_apply,2, "ac"}, {"reduce",array_reduce,-2, "ac."}, {"filter",array_filter,2, "ac"}, {"indexof",array_indexof,2, "a.", NULL, true}, {"contains",array_contains,2, "a."}, {"each",container_each,2, "ac"}, {"findindex",container_findindex,2, "ac", NULL, true}, {"findvalue",container_findvalue,-2, "ac.", NULL, true}, {"totable",array_to_table,1, "a", NULL, true}, {"replace",array_replace_with,2, "aa"}, // deprecated, use replace_with {"replace_with",array_replace_with,2, "aa"}, {"hasindex",array_hasindex,2, "an", NULL, true}, {"hasvalue",array_contains,2, "a.", NULL, true}, {"clone",obj_clone, 1, "." }, {"is_frozen",obj_is_frozen, 1, "." }, {"swap",swap, 3, "ann" }, {NULL,(SQFUNCTION)0,0,NULL} }; // String type methods static SQInteger string_hash(HSQUIRRELVM v) { union { SQHash hash; SQInteger i; SQUnsignedInteger u; } convert; memset(&convert, 0, sizeof(convert)); convert.hash = _string(stack_get(v, 1))->_hash; if (convert.i < 0) convert.u = ~convert.u; sq_pushinteger(v, convert.i); return 1; } static SQInteger string_slice(HSQUIRRELVM v) { SQInteger sidx,eidx; SQObjectPtr o; if(SQ_FAILED(get_slice_params(v,sidx,eidx,o)))return -1; SQInteger slen = _string(o)->_len; sidx = clamp_int((sidx < 0) ? (slen + sidx) : sidx, 0, slen); eidx = clamp_int((eidx < 0) ? (slen + eidx) : eidx, 0, slen); if (slen == 0 || eidx <= sidx) { v->Push(SQObjectPtr(SQString::Create(_ss(v), "", -1))); return 1; } v->Push(SQObjectPtr(SQString::Create(_ss(v),&_stringval(o)[sidx],eidx-sidx))); return 1; } static SQInteger string_hasindex(HSQUIRRELVM v) { const SQObjectPtr &o = stack_get(v, 1); SQInteger idx = tointeger(stack_get(v, 2)); sq_pushbool(v, idx >= 0 && idx < _string(o)->_len); return 1; } static SQInteger _string_scan_for_substring(HSQUIRRELVM v, SQInteger (*push_result)(HSQUIRRELVM v, SQInteger index)) { SQInteger top,start_idx=0; const char *str,*substr,*ret; if(((top=sq_gettop(v))>1) && SQ_SUCCEEDED(sq_getstring(v,1,&str)) && SQ_SUCCEEDED(sq_getstring(v,2,&substr))){ if (sq_getsize(v,2)<1) return sq_throwerror(v, "empty substring"); if(top>2)sq_getinteger(v,3,&start_idx); if((sq_getsize(v,1)>start_idx) && (start_idx>=0)){ ret=strstr(&str[start_idx],substr); if(ret) return push_result(v, ret-str); } return push_result(v, -1); } return sq_throwerror(v,"invalid param"); } static SQInteger string_indexof(HSQUIRRELVM v) { return _string_scan_for_substring(v, _push_scan_index); } static SQInteger string_contains(HSQUIRRELVM v) { return _string_scan_for_substring(v, _push_scan_found_flag); } static char* replace_all(SQAllocContext allocctx, char *s, SQInteger &buf_len, SQInteger &len, const char *from, SQInteger len_from, const char *to, SQInteger len_to) { for (SQInteger pos=0; pos<=len-len_from; ) { if (memcmp(s+pos, from, len_from*sizeof(char))==0) { SQInteger d_size = len_to - len_from; if (d_size > 0) { s = (char*)sq_realloc(allocctx, s, buf_len*sizeof(char), (buf_len+d_size)*sizeof(char)); buf_len += d_size; } if (d_size!=0) memmove(s+pos+len_to, s+pos+len_from, (len-pos-len_from)*sizeof(char)); memcpy(s+pos, to, len_to*sizeof(char)); len += d_size; pos += len_to; } else ++pos; } return s; } static char* replace_substring_internal(SQAllocContext allocctx, char *s, SQInteger &buf_len, SQInteger &len, int pos, SQInteger len_from, const char *to, SQInteger len_to) { SQInteger d_size = len_to - len_from; if (d_size > 0) { s = (char*)sq_realloc(allocctx, s, buf_len * sizeof(char), (buf_len + d_size) * sizeof(char)); buf_len += d_size; } if (d_size != 0) memmove(s + pos + len_to, s + pos + len_from, (len - pos - len_from) * sizeof(char)); memcpy(s + pos, to, len_to * sizeof(char)); len += d_size; return s; } static SQInteger string_substitute(HSQUIRRELVM v) { SQAllocContext allocctx = _ss(v)->_alloc_ctx; const char *fmt; SQInteger len; sq_getstringandsize(v, 1, &fmt, &len); SQInteger buf_len = len; char *s = (char *)sq_malloc(allocctx, len*sizeof(char)); memcpy(s, fmt, len * sizeof(char)); SQInteger top = sq_gettop(v); for (int i = 0; i < int(len) - 2; i++) if (s[i] == '{') { int depth = 0; for (int j = i + 1; j < len; j++) { if (s[j] == '}') { depth--; if (depth < 0) { if (i + 1 == j) break; int index = 0; for (int k = i + 1; k < j; k++) if (s[k] >= '0' && s[k] <= '9') index = index * 10 + s[k] - '0'; else { index = -1; break; } if (index >= 0) { index += 2; if (index <= top) { SQObjectPtr &val = stack_get(v, index); SQObjectPtr valStr; if (v->ToString(val, valStr)) { int delta = (int)_string(valStr)->_len - (j + 1 - i); s = replace_substring_internal(allocctx, s, buf_len, len, i, j + 1 - i, _stringval(valStr), _string(valStr)->_len); i = j + delta; break; } else { sq_free(allocctx, s, buf_len * sizeof(char)); return sq_throwerror(v, "subst: Failed to convert value to string"); } } } for (int idx = 2; idx <= top; idx++) { SQObjectPtr &arg = stack_get(v, idx); if (sq_type(arg) == OT_TABLE) { SQTable *table = _table(arg); SQObjectPtr val; SQObjectPtr valStr; if (table->GetStr(s + i + 1, j - i - 1, val)) { if (v->ToString(val, valStr)) { int delta = (int)_string(valStr)->_len - (j + 1 - i); s = replace_substring_internal(allocctx, s, buf_len, len, i, j + 1 - i, _stringval(valStr), _string(valStr)->_len); i = j + delta; break; } else { sq_free(allocctx, s, buf_len * sizeof(char)); return sq_throwerror(v, "subst: Failed to convert value to string"); } } } } break; // depth < 0 } } else if (s[j] == '{') depth++; } } sq_pushstring(v, s, len); sq_free(allocctx, s, buf_len * sizeof(char)); return 1; } static SQInteger string_replace(HSQUIRRELVM v) { const char *s, *from, *to; SQInteger len_s, len_from, len_to; sq_getstringandsize(v, 1, &s, &len_s); sq_getstringandsize(v, 2, &from, &len_from); sq_getstringandsize(v, 3, &to, &len_to); if (len_from <= 0) { sq_push(v, 1); return 1; } // can be optimized by avoiding unnecessary copying SQInteger buf_len = len_s; char *dest = (char *)sq_malloc(_ss(v)->_alloc_ctx, buf_len*sizeof(char)); SQInteger len_dest = len_s; memcpy(dest, s, len_s*sizeof(char)); dest = replace_all(_ss(v)->_alloc_ctx, dest, buf_len, len_dest, from, len_from, to, len_to); sq_pushstring(v, dest, len_dest); sq_free(_ss(v)->_alloc_ctx, dest, buf_len*sizeof(char)); return 1; } static SQInteger buf_concat(sqvector &res, const SQObjectPtrVec &strings, const char *sep, SQInteger sep_len) { SQInteger out_pos = 0; for (SQInteger i=0, nitems=strings.size(); i_val, s->_len*sizeof(char)); out_pos += s->_len; } return out_pos; } static SQInteger string_join(HSQUIRRELVM v) { const char *sep; SQInteger sep_len; sq_getstringandsize(v, 1, &sep, &sep_len); SQArray *arr = _array(stack_get(v, 2)); SQObjectPtrVec strings(_ss(v)->_alloc_ctx); strings.reserve(arr->Size()); SQObjectPtr tmp; SQObjectPtr flt; if (sq_gettop(v) > 3) return sq_throwerror(v, "Too many arguments"); if (sq_gettop(v) == 3) flt = stack_get(v, 3); SQInteger res_len = 0; for (SQInteger i=0; iSize(); ++i) { SQObjectPtr &item = arr->_values[i]; if (sq_isbool(flt) && sq_objtobool(&flt)) { if (sq_isnull(item) || (sq_isstring(item) && !_string(item)->_len)) continue; } else if (sq_isclosure(flt) || sq_isnativeclosure(flt)) { sq_push(v, 1); sq_pushobject(v, item); if (SQ_FAILED(sq_call(v,2,SQTrue,SQ_BASELIB_INVOKE_CB_ERR_HANDLER))) return SQ_ERROR; bool use = !SQVM::IsFalse(v->GetUp(-1)); v->Pop(); if (!use) continue; } if (!v->ToString(item, tmp)) return sq_throwerror(v, "Failed to convert array item to string"); strings.push_back(tmp); res_len += _string(tmp)->_len; } if (strings.empty()) { sq_pushstring(v, "", 0); return 1; } res_len += sep_len * (strings.size()-1); sqvector res(_ss(v)->_alloc_ctx); res.resize(res_len); SQInteger out_pos = buf_concat(res, strings, sep, sep_len); (void)out_pos; assert(out_pos == res_len); sq_pushstring(v, res._vals, res_len); return 1; } static SQInteger string_concat(HSQUIRRELVM v) { const char *sep; SQInteger sep_len; sq_getstringandsize(v, 1, &sep, &sep_len); SQInteger nitems = sq_gettop(v)-1; if (nitems < 1) { sq_pushstring(v, "", 0); return 1; } SQObjectPtrVec strings(_ss(v)->_alloc_ctx); strings.resize(nitems); SQInteger res_len = sep_len * (nitems-1); for (SQInteger i=0; iToString(stack_get(v, i+2), strings[i])) return sq_throwerror(v, "Failed to convert array item to string"); res_len += _string(strings[i])->_len; } sqvector res(_ss(v)->_alloc_ctx); res.resize(res_len); SQInteger out_pos = buf_concat(res, strings, sep, sep_len); (void)out_pos; assert(out_pos == res_len); sq_pushstring(v, res._vals, res_len); return 1; } static SQInteger string_split(HSQUIRRELVM v) { const char *str; SQInteger len; sq_getstringandsize(v, 1, &str, &len); SQArray *res = SQArray::Create(_ss(v),0); if (sq_gettop(v) == 1) { SQInteger start = 0; for (SQInteger pos=0; pos<=len; ++pos) { if (pos==len || sq_isspace(str[pos])) { if (pos > start) res->Append(SQObjectPtr(SQString::Create(_ss(v), str+start, pos-start))); start = pos+1; } } } else { const char *sep; SQInteger sep_len; sq_getstringandsize(v, 2, &sep, &sep_len); if (sep_len < 1) return sq_throwerror(v, "empty separator"); SQInteger start = 0; for (SQInteger pos=0; pos<=len;) { if (pos>len-sep_len) { res->Append(SQObjectPtr(SQString::Create(_ss(v), str+start, len-start))); break; } else if (strncmp(str+pos, sep, sep_len)==0) { res->Append(SQObjectPtr(SQString::Create(_ss(v), str+start, pos-start))); pos += sep_len; start = pos; } else ++pos; } } v->Push(SQObjectPtr(res)); return 1; } #define STRING_TOFUNCZ(func, call_func) static SQInteger string_##func(HSQUIRRELVM v) \ {\ SQInteger sidx,eidx; \ SQObjectPtr str; \ if(SQ_FAILED(get_slice_params(v,sidx,eidx,str)))return -1; \ SQInteger slen = _string(str)->_len; \ if(sidx < 0)sidx = slen + sidx; \ if(eidx < 0)eidx = slen + eidx; \ if(eidx < sidx) return sq_throwerror(v,"wrong indexes"); \ if(eidx > slen || sidx < 0) return sq_throwerror(v,"slice out of range"); \ SQInteger len=_string(str)->_len; \ const char *sthis=_stringval(str); \ char *snew=(_ss(v)->GetScratchPad(len)); \ memcpy(snew,sthis,len);\ for(SQInteger i=sidx;iPush(SQObjectPtr(SQString::Create(_ss(v),snew,len))); \ return 1; \ } STRING_TOFUNCZ(tolower, sq_tolower) STRING_TOFUNCZ(toupper, sq_toupper) #define IMPL_STRING_FUNC(name) static SQInteger _baselib_string_##name(HSQUIRRELVM v) { return _sq_string_ ## name ## _impl(v, 1); } IMPL_STRING_FUNC(strip) IMPL_STRING_FUNC(lstrip) IMPL_STRING_FUNC(rstrip) IMPL_STRING_FUNC(split_by_chars) IMPL_STRING_FUNC(escape) IMPL_STRING_FUNC(startswith) IMPL_STRING_FUNC(endswith) #undef IMPL_STRING_FUNC #define _DECL_FUNC(name,nparams,pmask) {#name,_baselib_string_##name,nparams,pmask} const SQRegFunction SQSharedState::_string_default_type_methods_funcz[]={ {"constructor",builtin_string_ctor,2, NULL}, {"len",default_type_method_len,1, "s", NULL, true}, {"tointeger",default_type_method_tointeger,-1, "sn", NULL, true}, {"tofloat",default_type_method_tofloat,1, "s", NULL, true}, {"tostring",default_type_method_tostring,1, ".", NULL, true}, {"hash",string_hash, 1, "s", NULL, true}, {"slice",string_slice,-1, "s n n", NULL, true}, {"indexof",string_indexof,-2, "s s n", NULL, true}, {"contains",string_contains,-2, "s s n", NULL, true}, {"hasindex",string_hasindex,-2, "s n", NULL, true}, {"tolower",string_tolower,-1, "s n n", NULL, true}, {"toupper",string_toupper,-1, "s n n", NULL, true}, {"subst",string_substitute,-2, "s", NULL, true}, {"replace",string_replace, 3, "s s s", NULL, true}, {"join",string_join, -2, "s a b|c", NULL, true}, {"concat",string_concat, -2, "s.", NULL, true}, {"split",string_split, -1, "s s", NULL, true}, {"weakref",obj_type_method_weakref,1, NULL }, {"clone",obj_clone, 1, "." }, _DECL_FUNC(strip,1,"s"), _DECL_FUNC(lstrip,1,"s"), _DECL_FUNC(rstrip,1,"s"), _DECL_FUNC(split_by_chars,-2,"ssb"), _DECL_FUNC(escape,1,"s"), _DECL_FUNC(startswith,2,"ss"), _DECL_FUNC(endswith,2,"ss"), {NULL,(SQFUNCTION)0,0,NULL} }; #undef _DECL_FUNC static SQInteger builtin_integer_ctor(HSQUIRRELVM v) { SQInteger nparams = sq_gettop(v); if (nparams == 1) { v->Push(SQObjectPtr((SQInteger)0)); return 1; } SQObjectPtr &o = stack_get(v, 2); SQInteger base = 10; if (nparams > 2) { sq_getinteger(v, 3, &base); } switch (sq_type(o)) { case OT_INTEGER: v->Push(o); return 1; case OT_FLOAT: v->Push(SQObjectPtr(tointeger(o))); return 1; case OT_STRING: { SQObjectPtr res; if (sq_parse_int(_stringval(o), _stringval(o) + _string(o)->_len, res, base)) { v->Push(SQObjectPtr(tointeger(res))); return 1; } return sq_throwerror(v, "cannot convert string to Integer"); } case OT_BOOL: v->Push(SQObjectPtr(_integer(o) ? (SQInteger)1 : (SQInteger)0)); return 1; default: return sq_throwerror(v, "cannot convert to Integer"); } } static SQInteger builtin_float_ctor(HSQUIRRELVM v) { SQInteger nparams = sq_gettop(v); if (nparams == 1) { v->Push(SQObjectPtr((SQFloat)0.0)); return 1; } SQObjectPtr &o = stack_get(v, 2); switch (sq_type(o)) { case OT_FLOAT: v->Push(o); return 1; case OT_INTEGER: v->Push(SQObjectPtr(tofloat(o))); return 1; case OT_STRING: { SQObjectPtr res; if (sq_parse_float(_stringval(o), _stringval(o) + _string(o)->_len, res, 10)) { v->Push(SQObjectPtr(tofloat(res))); return 1; } return sq_throwerror(v, "cannot convert string to Float"); } case OT_BOOL: v->Push(SQObjectPtr((SQFloat)(_integer(o) ? 1.0 : 0.0))); return 1; default: return sq_throwerror(v, "cannot convert to Float"); } } static SQInteger builtin_bool_ctor(HSQUIRRELVM v) { SQInteger nparams = sq_gettop(v); if (nparams == 1) { v->Push(SQObjectPtr(false)); return 1; } v->Push(SQObjectPtr(!SQVM::IsFalse(stack_get(v, 2)))); return 1; } static SQInteger builtin_string_ctor(HSQUIRRELVM v) { SQObjectPtr res; if (v->ToString(stack_get(v, 2), res)) { v->Push(res); return 1; } return sq_throwerror(v, "cannot convert to String"); } static SQInteger builtin_table_ctor(HSQUIRRELVM v) { SQTable *tbl = SQTable::Create(_ss(v), 0); v->Push(SQObjectPtr(tbl)); return 1; } static SQInteger builtin_null_ctor(HSQUIRRELVM v) { return 0; } static SQInteger builtin_weakref_ctor(HSQUIRRELVM v) { sq_weakref(v, 2); return 1; } // Null type methods const SQRegFunction SQSharedState::_null_default_type_methods_funcz[]={ {"constructor",builtin_null_ctor,-1, NULL}, {NULL,(SQFUNCTION)0,0,NULL} }; // Integer type methods const SQRegFunction SQSharedState::_integer_default_type_methods_funcz[]={ {"constructor",builtin_integer_ctor,-1, NULL}, {"tointeger",default_type_method_tointeger,1, "n|b", NULL, true}, {"tofloat",default_type_method_tofloat,1, "n|b", NULL, true}, {"tostring",default_type_method_tostring,1, ".", NULL, true}, {"tochar",number_type_method_tochar,1, "n|b", NULL, true}, {"weakref",obj_type_method_weakref,1, NULL }, {"clone",obj_clone, 1, "." }, {NULL,(SQFUNCTION)0,0,NULL} }; // Float type methods const SQRegFunction SQSharedState::_float_default_type_methods_funcz[]={ {"constructor",builtin_float_ctor,-1, NULL}, {"tointeger",default_type_method_tointeger,1, "n|b", NULL, true}, {"tofloat",default_type_method_tofloat,1, "n|b", NULL, true}, {"tostring",default_type_method_tostring,1, ".", NULL, true}, {"tochar",number_type_method_tochar,1, "n|b", NULL, true}, {"weakref",obj_type_method_weakref,1, NULL }, {"clone",obj_clone, 1, "." }, {NULL,(SQFUNCTION)0,0,NULL} }; // Bool type methods const SQRegFunction SQSharedState::_bool_default_type_methods_funcz[]={ {"constructor",builtin_bool_ctor,-1, NULL}, {"tointeger",default_type_method_tointeger,1, "n|b", NULL, true}, {"tofloat",default_type_method_tofloat,1, "n|b", NULL, true}, {"tostring",default_type_method_tostring,1, ".", NULL, true}, {"tochar",number_type_method_tochar,1, "n|b", NULL, true}, {"weakref",obj_type_method_weakref,1, NULL }, {"clone",obj_clone, 1, "." }, {NULL,(SQFUNCTION)0,0,NULL} }; // Closure type methods static SQInteger closure_pcall(HSQUIRRELVM v) { return SQ_SUCCEEDED(sq_call(v,sq_gettop(v)-1,SQTrue,SQFalse))?1:SQ_ERROR; } static SQInteger closure_call(HSQUIRRELVM v) { SQObjectPtr &c = stack_get(v, -1); if (sq_type(c) == OT_CLOSURE && (_closure(c)->_function->_bgenerator == false)) { return sq_tailcall(v, sq_gettop(v) - 1); } return SQ_SUCCEEDED(sq_call(v, sq_gettop(v) - 1, SQTrue, SQTrue)) ? 1 : SQ_ERROR; } static SQInteger _closure_acall(HSQUIRRELVM v,SQBool invoke_err_handler) { SQArray *aparams=_array(stack_get(v,2)); SQInteger nparams=aparams->Size(); v->Push(stack_get(v,1)); for(SQInteger i=0;iPush(aparams->_values[i]); return SQ_SUCCEEDED(sq_call(v,nparams,SQTrue,invoke_err_handler))?1:SQ_ERROR; } static SQInteger closure_acall(HSQUIRRELVM v) { return _closure_acall(v,SQTrue); } static SQInteger closure_pacall(HSQUIRRELVM v) { return _closure_acall(v,SQFalse); } static SQInteger closure_bindenv(HSQUIRRELVM v) { if(SQ_FAILED(sq_bindenv(v,1))) return SQ_ERROR; return 1; } static SQInteger closure_getfreevar(HSQUIRRELVM v) { SQInteger varidx; sq_getinteger(v, 2, &varidx); const char *name = sq_getfreevariable(v, 1, SQUnsignedInteger(varidx)); if (!name) return sq_throwerror(v, "Invalid free variable index"); SQObjectPtr val = v->PopGet(); SQTable *res = SQTable::Create(_ss(v),2); res->NewSlot(SQObjectPtr(SQString::Create(_ss(v),"name",-1)), SQObjectPtr(SQString::Create(_ss(v),name,-1))); res->NewSlot(SQObjectPtr(SQString::Create(_ss(v),"value",-1)), val); v->Push(SQObjectPtr(res)); return 1; } static SQInteger closure_getfuncinfos_obj(HSQUIRRELVM v, SQObjectPtr & o) { SQTable *res = SQTable::Create(_ss(v), 20); #define SET_SLOT(name, value) \ res->NewSlot(SQObjectPtr(SQString::Create(_ss(v), name, -1)), SQObjectPtr(value)) if(sq_type(o) == OT_CLOSURE) { SQFunctionProto *f = _closure(o)->_function; SQInteger nparams = f->_nparameters - (f->_varparams ? 1 : 0); SQObjectPtr params(SQArray::Create(_ss(v),nparams)); SQObjectPtr typecheck(SQArray::Create(_ss(v),nparams)); SQObjectPtr defparams(SQArray::Create(_ss(v),f->_ndefaultparams)); for(SQInteger n = 0; n < nparams; n++) { _array(params)->Set(n, f->_parameters[n]); _array(typecheck)->Set(n, SQObjectPtr(int(f->_param_type_masks[n]))); } for(SQInteger j = 0; j < f->_ndefaultparams; j++) { _array(defparams)->Set((SQInteger)j,_closure(o)->_defaultparams[j]); } SET_SLOT("native", false); SET_SLOT("pure", f->_purefunction); SET_SLOT("nodiscard", f->_nodiscard); SET_SLOT("name", f->_name); SET_SLOT("freevars", f->_noutervalues); SET_SLOT("src", f->_sourcename); SET_SLOT("line", SQInteger(f->_lineinfos->_first_line)); SET_SLOT("parameters", params); SET_SLOT("defparams", defparams); SET_SLOT("required_params", nparams - f->_ndefaultparams); SET_SLOT("varargs", bool(f->_varparams)); SET_SLOT("typecheck", typecheck); SET_SLOT("return_type_mask", SQObjectPtr(int(f->_result_type_mask))); SET_SLOT("varargs_type_mask", SQObjectPtr(int(f->_varparams ? f->_param_type_masks[f->_nparameters - 1] : 0))); SQObjectPtr key; SQObjectPtr docObject; key._type = OT_USERPOINTER; key._unVal.pUserPointer = (void *)_closure(o)->_function; _table(_ss(v)->doc_objects)->Get(key, docObject); SET_SLOT("doc", docObject); } else { //OT_NATIVECLOSURE SQNativeClosure *nc = _nativeclosure(o); int requiredParams = nc->_nparamscheck == 0 ? 1 : abs(nc->_nparamscheck); bool varargs = nc->_nparamscheck <= 0; SQInteger varargsTypeMask = varargs ? -1 : 0; SQObjectPtr parameters; SQObjectPtr defparams; SQObjectPtr typecheck; SQObjectPtr key; SQObjectPtr value; key._type = OT_USERPOINTER; key._unVal.pUserPointer = (void *)((size_t)(void *)nc->_function ^ ~size_t(0)); if (_table(_ss(v)->doc_objects)->Get(key, value)) { SQFunctionType ft(_ss(v)); SQInteger errorPos = -1; SQObjectPtr errorString; if (sq_isstring(value)) { if (sq_parse_function_type_string(v, _stringval(value), ft, errorPos, errorString)) { parameters = SQObjectPtr(SQArray::Create(_ss(v), ft.argNames.size() + 1)); _array(parameters)->Set(SQInteger(0), SQObjectPtr(SQString::Create(_ss(v), "this", -1))); for (SQUnsignedInteger n = 0; n < ft.argNames.size(); n++) { _array(parameters)->Set((SQInteger)n + 1, SQObjectPtr(ft.argNames[n])); } int defParamsCount = ft.argNames.size() - ft.requiredArgs; defparams = SQObjectPtr(SQArray::Create(_ss(v), defParamsCount)); for (SQUnsignedInteger n = 0; n < defParamsCount; n++) { _array(parameters)->Set((SQInteger)n, SQObjectPtr()); } varargs = ft.ellipsisArgTypeMask != 0; varargsTypeMask = int(ft.ellipsisArgTypeMask); } else { // TODO: raise errorString } } } else { parameters = SQObjectPtr(SQArray::Create(_ss(v), requiredParams)); _array(parameters)->Set(SQInteger(0), SQObjectPtr(SQString::Create(_ss(v), "this", -1))); for (SQInteger n = 1; n < requiredParams; n++) { char buf[16] = { 0 }; snprintf(buf, sizeof(buf), "arg%d", int(n)); _array(parameters)->Set((SQInteger)n, SQObjectPtr(SQString::Create(_ss(v), buf, -1))); } defparams = SQObjectPtr(SQArray::Create(_ss(v), 0)); } typecheck = SQObjectPtr(SQArray::Create(_ss(v), nc->_typecheck.size())); for (SQUnsignedInteger n = 0; n < nc->_typecheck.size(); n++) { _array(typecheck)->Set((SQInteger)n, SQObjectPtr(int(nc->_typecheck[n]))); } SET_SLOT("native", true); SET_SLOT("pure", nc->_purefunction); SET_SLOT("nodiscard", nc->_nodiscard); SET_SLOT("name", nc->_name); SET_SLOT("freevars", SQInteger(nc->_noutervalues)); SET_SLOT("src", SQObjectPtr()); SET_SLOT("line", SQObjectPtr()); SET_SLOT("parameters", parameters); SET_SLOT("defparams", defparams); SET_SLOT("required_params", requiredParams); SET_SLOT("paramscheck", nc->_nparamscheck); SET_SLOT("varargs", varargs); SET_SLOT("typecheck", typecheck); SET_SLOT("return_type_mask", SQObjectPtr(int(nc->_result_type_mask))); SET_SLOT("varargs_type_mask", varargsTypeMask); SQObjectPtr docObject; key._unVal.pUserPointer = (void *)nc->_function; _table(_ss(v)->doc_objects)->Get(key, docObject); SET_SLOT("doc", docObject); } #undef SET_SLOT v->Push(SQObjectPtr(res)); return 1; } static SQInteger closure_getfuncinfos(HSQUIRRELVM v) { SQObjectPtr o = stack_get(v, 1); return closure_getfuncinfos_obj(v, o); } static SQInteger delegable_getfuncinfos(HSQUIRRELVM v) { SQObjectPtr o = stack_get(v, 1); SQDelegable * delegable = _delegable(o); SQObjectPtr call; if (delegable->GetMetaMethod(v, MT_CALL, call)) closure_getfuncinfos_obj(v, call); else sq_pushnull(v); return 1; } static SQInteger class_getfuncinfos(HSQUIRRELVM v) { SQObjectPtr o = stack_get(v, 1); if (!sq_isnull(_class(o)->_metamethods[MT_CALL])) closure_getfuncinfos_obj(v, _class(o)->_metamethods[MT_CALL]); else sq_pushnull(v); return 1; } const SQRegFunction SQSharedState::_closure_default_type_methods_funcz[]={ {"call",closure_call,-1, "c"}, {"pcall",closure_pcall,-1, "c"}, {"acall",closure_acall,2, "ca"}, {"pacall",closure_pacall,2, "ca"}, {"weakref",obj_type_method_weakref,1, NULL }, {"tostring",default_type_method_tostring,1, "."}, {"bindenv",closure_bindenv,2, "c x|y|t"}, {"getfuncinfos",closure_getfuncinfos,1, "c"}, {"getfreevar",closure_getfreevar,2, "ci"}, {"clone",obj_clone, 1, "." }, {NULL,(SQFUNCTION)0,0,NULL} }; // Generator type methods static SQInteger generator_getstatus(HSQUIRRELVM v) { SQObject &o=stack_get(v,1); switch(_generator(o)->_state){ case SQGenerator::eSuspended:v->Push(SQObjectPtr(SQString::Create(_ss(v),"suspended")));break; case SQGenerator::eRunning:v->Push(SQObjectPtr(SQString::Create(_ss(v),"running")));break; case SQGenerator::eDead:v->Push(SQObjectPtr(SQString::Create(_ss(v),"dead")));break; } return 1; } const SQRegFunction SQSharedState::_generator_default_type_methods_funcz[]={ {"getstatus",generator_getstatus,1, "g"}, {"weakref",obj_type_method_weakref,1, NULL }, {"tostring",default_type_method_tostring,1, "."}, {"clone",obj_clone, 1, "." }, {NULL,(SQFUNCTION)0,0,NULL} }; // Thead type methods static SQInteger thread_call(HSQUIRRELVM v) { SQObjectPtr o = stack_get(v,1); if(sq_type(o) == OT_THREAD) { SQInteger nparams = sq_gettop(v); sq_reservestack(_thread(o), nparams + 3); _thread(o)->Push(_thread(o)->_roottable); for(SQInteger i = 2; i<(nparams+1); i++) sq_move(_thread(o),v,i); if(SQ_SUCCEEDED(sq_call(_thread(o),nparams,SQTrue,SQTrue))) { sq_move(v,_thread(o),-1); sq_pop(_thread(o),1); return 1; } v->_lasterror = _thread(o)->_lasterror; return SQ_ERROR; } return sq_throwerror(v,"wrong parameter"); } static SQInteger thread_wakeup(HSQUIRRELVM v) { SQObjectPtr o = stack_get(v,1); if(sq_type(o) == OT_THREAD) { SQVM *thread = _thread(o); SQInteger state = sq_getvmstate(thread); if(state != SQ_VMSTATE_SUSPENDED) { switch(state) { case SQ_VMSTATE_IDLE: return sq_throwerror(v,"cannot wakeup a idle thread"); break; case SQ_VMSTATE_RUNNING: return sq_throwerror(v,"cannot wakeup a running thread"); break; } } SQInteger wakeupret = sq_gettop(v)>1?SQTrue:SQFalse; if(wakeupret) { sq_move(thread,v,2); } if(SQ_SUCCEEDED(sq_wakeupvm(thread,wakeupret,SQTrue,SQTrue,SQFalse))) { sq_move(v,thread,-1); sq_pop(thread,1); //pop retval if(sq_getvmstate(thread) == SQ_VMSTATE_IDLE) { sq_settop(thread,1); //pop roottable } return 1; } sq_settop(thread,1); v->_lasterror = thread->_lasterror; return SQ_ERROR; } return sq_throwerror(v,"wrong parameter"); } static SQInteger thread_wakeupthrow(HSQUIRRELVM v) { SQObjectPtr o = stack_get(v,1); if(sq_type(o) == OT_THREAD) { SQVM *thread = _thread(o); SQInteger state = sq_getvmstate(thread); if(state != SQ_VMSTATE_SUSPENDED) { switch(state) { case SQ_VMSTATE_IDLE: return sq_throwerror(v,"cannot wakeup a idle thread"); break; case SQ_VMSTATE_RUNNING: return sq_throwerror(v,"cannot wakeup a running thread"); break; } } sq_move(thread,v,2); sq_throwobject(thread); SQBool rethrow_error = SQTrue; if(sq_gettop(v) > 2) { sq_getbool(v,3,&rethrow_error); } if(SQ_SUCCEEDED(sq_wakeupvm(thread,SQFalse,SQTrue,SQTrue,SQTrue))) { sq_move(v,thread,-1); sq_pop(thread,1); //pop retval if(sq_getvmstate(thread) == SQ_VMSTATE_IDLE) { sq_settop(thread,1); //pop roottable } return 1; } sq_settop(thread,1); if(rethrow_error) { v->_lasterror = thread->_lasterror; return SQ_ERROR; } return SQ_OK; } return sq_throwerror(v,"wrong parameter"); } static SQInteger thread_getstatus(HSQUIRRELVM v) { SQObjectPtr &o = stack_get(v,1); switch(sq_getvmstate(_thread(o))) { case SQ_VMSTATE_IDLE: sq_pushstring(v,"idle",-1); break; case SQ_VMSTATE_RUNNING: sq_pushstring(v,"running",-1); break; case SQ_VMSTATE_SUSPENDED: sq_pushstring(v,"suspended",-1); break; default: return sq_throwerror(v,"internal VM error"); } return 1; } static SQInteger thread_getstackinfos(HSQUIRRELVM v) { SQObjectPtr o = stack_get(v,1); if(sq_type(o) == OT_THREAD) { SQVM *thread = _thread(o); SQInteger threadtop = sq_gettop(thread); SQInteger level; sq_getinteger(v,-1,&level); SQRESULT res = __sq_getcallstackinfos(thread,level); if(SQ_FAILED(res)) { sq_settop(thread,threadtop); if(sq_type(thread->_lasterror) == OT_STRING) { sq_throwerror(v,_stringval(thread->_lasterror)); } else { sq_throwerror(v,"unknown error"); } } if(res > 0) { //some result sq_move(v,thread,-1); sq_settop(thread,threadtop); return 1; } //no result sq_settop(thread,threadtop); return 0; } return sq_throwerror(v,"wrong parameter"); } const SQRegFunction SQSharedState::_thread_default_type_methods_funcz[] = { {"call", thread_call, -1, "v"}, {"wakeup", thread_wakeup, -1, "v"}, {"wakeupthrow", thread_wakeupthrow, -2, "v.b"}, {"getstatus", thread_getstatus, 1, "v"}, {"weakref",obj_type_method_weakref,1, NULL }, {"getstackinfos",thread_getstackinfos,2, "vn"}, {"tostring",default_type_method_tostring,1, "."}, {"clone",obj_clone, 1, "." }, {NULL,(SQFUNCTION)0,0,NULL} }; static SQInteger class_instance(HSQUIRRELVM v) { return SQ_SUCCEEDED(sq_createinstance(v,-1))?1:SQ_ERROR; } static SQInteger class_getbase(HSQUIRRELVM v) { return SQ_SUCCEEDED(sq_getbase(v,-1))?1:SQ_ERROR; } static SQInteger class_newmember(HSQUIRRELVM v) { SQInteger top = sq_gettop(v); SQBool bstatic = SQFalse; if(top == 5) { sq_tobool(v,-1,&bstatic); sq_pop(v,1); } if(top < 4) { sq_pushnull(v); } return SQ_SUCCEEDED(sq_newmember(v,-4,bstatic))?1:SQ_ERROR; } static SQInteger get_class_metamethod(HSQUIRRELVM v) { SQInteger mmidx = _ss(v)->GetMetaMethodIdxByName(stack_get(v, 2)); if (mmidx < 0) return sq_throwerror(v, "Unknown metamethod"); SQClass *cls = nullptr; if (sq_gettype(v, 1) == OT_CLASS) cls = _class(stack_get(v, 1)); else // instance cls = _instance(stack_get(v, 1))->_class; v->Push(cls->_metamethods[mmidx]); return 1; } static SQInteger class_lock(HSQUIRRELVM v) { SQClass *cls = _class(stack_get(v, 1)); if (!cls->isLocked()) { if (!cls->Lock(v)) return SQ_ERROR; // propagate raised error } return 1; } const SQRegFunction SQSharedState::_class_default_type_methods_funcz[] = { {"rawget",container_rawget,2, "y", NULL, true}, {"rawset",container_rawset,3, "y"}, {"rawin",container_rawexists,2, "y", NULL, true}, {"weakref",obj_type_method_weakref,1, NULL }, {"tostring",default_type_method_tostring,1, "."}, {"instance",class_instance,1, "y"}, {"getbase",class_getbase,1, "y", NULL, true}, {"newmember",class_newmember,-3, "y"}, {"getfuncinfos",class_getfuncinfos,1, "y"}, {"call",closure_call,-1, "y"}, {"pcall",closure_pcall,-1, "y"}, {"acall",closure_acall,2, "ya"}, {"pacall",closure_pacall,2, "ya"}, {"__update",container_update, -2, "t|yt|y|x", NULL, false}, {"__merge",container_merge, -2, "t|yt|y|x", NULL, true}, {"getmetamethod",get_class_metamethod, 2, "ys"}, {"hasindex",container_hasindex,2, "y.", NULL, true}, {"clone",obj_clone, 1, "." }, {"is_frozen",obj_is_frozen, 1, "." }, {"lock",class_lock, 1, "y" }, {NULL,(SQFUNCTION)0,0,NULL} }; static SQInteger instance_getclass(HSQUIRRELVM v) { if(SQ_SUCCEEDED(sq_getclass(v,1))) return 1; return SQ_ERROR; } const SQRegFunction SQSharedState::_instance_default_type_methods_funcz[] = { {"getclass", instance_getclass, 1, "x", NULL, true}, {"rawget",container_rawget,2, "x", NULL, true}, {"rawset",container_rawset,3, "x"}, {"rawin",container_rawexists,2, "x", NULL, true}, {"weakref",obj_type_method_weakref,1, NULL }, {"tostring",default_type_method_tostring,1, "."}, {"getfuncinfos",delegable_getfuncinfos,1, "x"}, {"getmetamethod",get_class_metamethod, 2, "xs"}, {"hasindex",container_hasindex,2, "x.", NULL, true}, {"clone",obj_clone, 1, "." }, {"is_frozen",obj_is_frozen, 1, "." }, {"swap",swap, 3, "x.." }, {NULL,(SQFUNCTION)0,0,NULL} }; static SQInteger weakref_ref(HSQUIRRELVM v) { if(SQ_FAILED(sq_getweakrefval(v,1))) return SQ_ERROR; return 1; } const SQRegFunction SQSharedState::_weakref_default_type_methods_funcz[] = { {"constructor",builtin_weakref_ctor, 2, NULL}, {"ref",weakref_ref,1, "r"}, {"weakref",obj_type_method_weakref,1, NULL }, {"tostring",default_type_method_tostring,1, "."}, {"clone",obj_clone, 1, "." }, {NULL,(SQFUNCTION)0,0,NULL} }; const SQRegFunction SQSharedState::_userdata_default_type_methods_funcz[] = { {"getfuncinfos",delegable_getfuncinfos,1, "u"}, {"clone",obj_clone, 1, "." }, {"is_frozen",obj_is_frozen, 1, "." }, {NULL,(SQFUNCTION)0,0,NULL} }; ================================================ FILE: squirrel/sqclass.cpp ================================================ /* see copyright notice in squirrel.h */ #include "sqpcheader.h" #include "sqvm.h" #include "sqtable.h" #include "sqclass.h" #include "sqfuncproto.h" #include "sqclosure.h" SQClass::SQClass(SQSharedState *ss, SQClass *base) : _defaultvalues(ss->_alloc_ctx), _methods(ss->_alloc_ctx), _nativefields(ss->_alloc_ctx) { _base = base; _typetag = 0; _hook = NULL; _udsize = 0; _constructoridx = -1; _lockedTypeId = 0; _is_builtin_type = false; _builtin_type_id = OT_NULL; if(_base) { _constructoridx = _base->_constructoridx; _udsize = _base->_udsize; _defaultvalues.copy(base->_defaultvalues); _methods.copy(base->_methods); _nativefields.copy(base->_nativefields); _COPY_VECTOR(_metamethods,base->_metamethods,MT_NUM_METHODS); __ObjAddRef(_base); } _members = base?base->_members->Clone() : SQTable::Create(ss,0); __ObjAddRef(_members); INIT_CHAIN(); ADD_TO_CHAIN(&_sharedstate->_gc_chain, this); } void SQClass::Finalize() { _NULL_SQOBJECT_VECTOR(_defaultvalues,_defaultvalues.size()); _methods.resize(0); _nativefields.resize(0); _NULL_SQOBJECT_VECTOR(_metamethods,MT_NUM_METHODS); __ObjRelease(_members); if(_base) { __ObjRelease(_base); } } SQClass::~SQClass() { REMOVE_FROM_CHAIN(&_sharedstate->_gc_chain, this); Finalize(); } SQClass* SQClass::Create(SQVM *v,SQClass *base) { if (base) { if (base->_is_builtin_type) { v->Raise_Error("Cannot inherit from built-in type '%s'", IdType2Name(base->_builtin_type_id)); return nullptr; } if (!base->isLocked()) { if (!base->Lock(v)) return nullptr; } } SQClass *newclass = (SQClass *)SQ_MALLOC(_ss(v)->_alloc_ctx, sizeof(SQClass)); new (newclass) SQClass(_ss(v), base); return newclass; } bool SQClass::NewSlot(SQSharedState *ss,const SQObjectPtr &key,const SQObjectPtr &val,bool bstatic) { SQObjectPtr temp; bool belongs_to_static_table = sq_type(val) == OT_CLOSURE || sq_type(val) == OT_NATIVECLOSURE || bstatic; if(isLocked() && !belongs_to_static_table) return false; //the class already has an instance so cannot be modified if(_members->Get(key,temp)) { if (_isfield(temp)) { //overrides the default value _defaultvalues[_member_idx(temp)].val = val; return true; } if (_isnativefield(temp)) return false; // native fields cannot be overridden from script } if (_members->CountUsed() >= MEMBER_MAX_COUNT) { return false; } if(belongs_to_static_table) { SQInteger mmidx; if((sq_type(val) == OT_CLOSURE || sq_type(val) == OT_NATIVECLOSURE) && (mmidx = ss->GetMetaMethodIdxByName(key)) != -1) { _metamethods[mmidx] = val; } else { SQObjectPtr theval = val; if(_base && sq_type(val) == OT_CLOSURE) { theval = _closure(val)->Clone(); _closure(theval)->_base = _base; __ObjAddRef(_base); //ref for the closure } if(sq_type(temp) == OT_NULL) { bool isconstructor = SQVM::IsEqual(ss->_constructorstr, key); if(isconstructor) { _constructoridx = (SQInteger)_methods.size(); } SQClassMember m; m.val = theval; _members->NewSlot(key,SQObjectPtr(_make_method_idx(_methods.size()))); _methods.push_back(m); } else { _methods[_member_idx(temp)].val = theval; } } return true; } SQClassMember m; m.val = val; _members->NewSlot(key,SQObjectPtr(_make_field_idx(_defaultvalues.size()))); _defaultvalues.push_back(m); return true; } SQInstance *SQClass::CreateInstance(SQVM *v) { if (!isLocked()) { if (!Lock(v)) return nullptr; } return SQInstance::Create(_opt_ss(this),this); } SQInteger SQClass::Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval) { SQObjectPtr oval; SQInteger idx = _members->Next(false,refpos,outkey,oval); if(idx != -1) { if(_isfield(oval)) { SQObjectPtr &o = _defaultvalues[_member_idx(oval)].val; outval = _realval(o); } else if(_isnativefield(oval)) { outval.Null(); // no instance context } else { outval = _methods[_member_idx(oval)].val; } } return idx; } bool SQClass::Lock(SQVM *v) { if (isLocked()) return true; if (_base) { if (!_base->Lock(v)) return false; } bool success = true; if (sq_type(_metamethods[MT_LOCK]) != OT_NULL) { SQInteger prevTop = v->_top; SQObjectPtr res; v->Push(SQObjectPtr(this)); success = v->Call(_metamethods[MT_LOCK], 1, v->_top-1, res, true); v->_top = prevTop; } _lockedTypeId = currentHint(); return success; } bool SQClass::RegisterNativeField(SQSharedState *ss, const SQObjectPtr &key, uint16_t offset, uint8_t type) { if (isLocked()) return false; if (_udsize <= 0) return false; static const uint8_t type_sizes[] = {4, 8, 4, 8, 1}; // float32, float64, int32, int64, bool if (type > SQNFT_BOOL || offset + type_sizes[type] > (uint32_t)_udsize) return false; if (_members->CountUsed() >= MEMBER_MAX_COUNT) return false; SQObjectPtr temp; if (_members->Get(key, temp)) return false; // name already taken SQNativeFieldDesc desc; desc.offset = offset; desc.type = type; _members->NewSlot(key, SQObjectPtr(_make_native_field_idx(_nativefields.size()))); _nativefields.push_back(desc); return true; } /////////////////////////////////////////////////////////////////////// void SQInstance::Init(SQSharedState *ss) { _alloc_ctx = ss->_alloc_ctx; _userpointer = NULL; _hook = NULL; __ObjAddRef(_class); _delegate = _class->_members; INIT_CHAIN(); ADD_TO_CHAIN(&_sharedstate->_gc_chain, this); } SQInstance::SQInstance(SQSharedState *ss, SQClass *c, SQInteger memsize) { _memsize = memsize; _class = c; SQUnsignedInteger nvalues = _class->_defaultvalues.size(); for(SQUnsignedInteger n = 0; n < nvalues; n++) { new (&_values[n]) SQObjectPtr(_class->_defaultvalues[n].val); } Init(ss); } SQInstance::SQInstance(SQSharedState *ss, SQInstance *i, SQInteger memsize) { _memsize = memsize; _class = i->_class; SQUnsignedInteger nvalues = _class->_defaultvalues.size(); for(SQUnsignedInteger n = 0; n < nvalues; n++) { new (&_values[n]) SQObjectPtr(i->_values[n]); } Init(ss); } void SQInstance::Finalize() { SQUnsignedInteger nvalues = _class->_defaultvalues.size(); __ObjRelease(_class); _NULL_SQOBJECT_VECTOR(_values,nvalues); } SQInstance::~SQInstance() { REMOVE_FROM_CHAIN(&_sharedstate->_gc_chain, this); if(_class){ Finalize(); } //if _class is null it was already finalized by the GC } bool SQInstance::GetMetaMethod(SQVM* SQ_UNUSED_ARG(v),SQMetaMethod mm,SQObjectPtr &res) { if(sq_type(_class->_metamethods[mm]) != OT_NULL) { res = _class->_metamethods[mm]; return true; } return false; } bool SQInstance::InstanceOf(const SQClass *trg) const { const SQClass *parent = _class; while(parent != NULL) { if(parent == trg) return true; parent = parent->_base; } return false; } ================================================ FILE: squirrel/sqclass.h ================================================ /* see copyright notice in squirrel.h */ #ifndef _SQCLASS_H_ #define _SQCLASS_H_ #define SLOT_STATUS_OK 0 #define SLOT_STATUS_NO_MATCH 1 #define SLOT_STATUS_ERROR 2 struct SQInstance; struct SQClassMember { SQObjectPtr val; void Null() { val.Null(); } }; typedef sqvector SQClassMemberVec; #define MEMBER_MAX_COUNT_BIT_SHIFT 22 #define MEMBER_TYPE_FIELD (0x01<Get(key,val)) { if(_isfield(val)) { SQObjectPtr &o = _defaultvalues[_member_idx(val)].val; val = _realval(o); } else if(_isnativefield(val)) { val.Null(); // no instance context } else { val = _methods[_member_idx(val)].val; } return true; } return false; } // for compiler use bool GetStr(const char *key, int keylen, SQObjectPtr &val) const { if(_members->GetStr(key, keylen, val)) { if(_isfield(val)) { SQObjectPtr &o = _defaultvalues[_member_idx(val)].val; val = _realval(o); } else if(_isnativefield(val)) { val.Null(); // no instance context } else { val = _methods[_member_idx(val)].val; } return true; } return false; } bool GetConstructor(SQObjectPtr &ctor) { if(_constructoridx != -1) { ctor = _methods[_constructoridx].val; return true; } return false; } bool Lock(SQVM *v); void Release() { if (_hook) { _hook(_thread(_sharedstate->_root_vm),_typetag,0);} SQAllocContext ctx = _methods._alloc_ctx; sq_delete(ctx, this, SQClass); } uint64_t lockedTypeId() const {return _lockedTypeId;} enum {CLASS_BITS = 40}; //all x64 bit platforms have only 48 meaningful bits in pointers. Yet, 3 lsb are always zero (or meaningless, as pointers are aligned) //actually, even more bits are meaningless - as any class can't alias with other, there should be at least sizeof(SQClass) difference //sizeof is at least 256 on all platforms, so there are no more than 40 meaningful bits (after first 8) //still more than 32, unfortunately bool isLocked() const {return _lockedTypeId != 0;} static uint64_t classTypeFromHint(uint64_t hint) { static_assert(sizeof(SQClass)>=256); return hint&((1ull<>uintptr_t(8));} void Finalize(); #ifndef NO_GARBAGE_COLLECTOR void Mark(SQCollectable ** ); SQObjectType GetType() {return OT_CLASS;} #endif SQInteger Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval); SQInstance *CreateInstance(SQVM *v); SQTable *_members; SQClass *_base; SQClassMemberVec _defaultvalues; SQClassMemberVec _methods; sqvector _nativefields; SQObjectPtr _metamethods[MT_NUM_METHODS]; SQUserPointer _typetag; SQRELEASEHOOK _hook; SQInteger _constructoridx; SQInteger _udsize; uint64_t _lockedTypeId; bool _is_builtin_type; SQObjectType _builtin_type_id; // only valid if _is_builtin_type is true }; #define calcinstancesize(_theclass_) \ (_theclass_->_udsize + sq_aligning(sizeof(SQInstance) + (sizeof(SQObjectPtr)*(_theclass_->_defaultvalues.size()>0?_theclass_->_defaultvalues.size()-1:0)))) struct SQInstance : public SQDelegable { void Init(SQSharedState *ss); SQInstance(SQSharedState *ss, SQClass *c, SQInteger memsize); SQInstance(SQSharedState *ss, SQInstance *c, SQInteger memsize); public: static SQInstance* Create(SQSharedState *ss,SQClass *theclass) { SQInteger size = calcinstancesize(theclass); SQInstance *newinst = (SQInstance *)SQ_MALLOC(ss->_alloc_ctx, size); new (newinst) SQInstance(ss, theclass,size); if(theclass->_udsize) { newinst->_userpointer = ((unsigned char *)newinst) + (size - theclass->_udsize); } return newinst; } SQInstance *Clone(SQSharedState *ss) { SQInteger size = calcinstancesize(_class); SQInstance *newinst = (SQInstance *)SQ_MALLOC(ss->_alloc_ctx, size); new (newinst) SQInstance(ss, this,size); if(_class->_udsize) { newinst->_userpointer = ((unsigned char *)newinst) + (size - _class->_udsize); } return newinst; } ~SQInstance(); // Pointer to the inline userdata area at the tail of the SQInstance allocation. // Computed from the allocation layout, immune to sq_setinstanceup() overrides. inline void *_inlineud() const { return ((unsigned char *)const_cast(this)) + (_memsize - _class->_udsize); } inline void GetNativeField(uint32_t fieldIdx, SQObjectPtr &val) const { const SQNativeFieldDesc &desc = _class->_nativefields[fieldIdx]; const char *p = (const char *)_inlineud() + desc.offset; switch (desc.type) { case SQNFT_FLOAT32: val = SQObjectPtr((SQFloat)*(const float *)p); break; case SQNFT_FLOAT64: val = SQObjectPtr((SQFloat)*(const double *)p); break; case SQNFT_INT32: val = SQObjectPtr((SQInteger)*(const int32_t *)p); break; case SQNFT_INT64: val = SQObjectPtr((SQInteger)*(const int64_t *)p); break; case SQNFT_BOOL: val._type = OT_BOOL; val._unVal.nInteger = *(const bool *)p ? 1 : 0; break; } } inline bool SetNativeField(uint32_t fieldIdx, const SQObjectPtr &val) { const SQNativeFieldDesc &desc = _class->_nativefields[fieldIdx]; char *p = (char *)_inlineud() + desc.offset; SQObjectType vt = sq_type(val); switch (desc.type) { case SQNFT_FLOAT32: if (vt == OT_FLOAT) *(float *)p = (float)_float(val); else if (vt == OT_INTEGER) *(float *)p = (float)_integer(val); else return false; break; case SQNFT_FLOAT64: if (vt == OT_FLOAT) *(double *)p = (double)_float(val); else if (vt == OT_INTEGER) *(double *)p = (double)_integer(val); else return false; break; case SQNFT_INT32: if (vt == OT_INTEGER) *(int32_t *)p = (int32_t)_integer(val); else if (vt == OT_FLOAT) *(int32_t *)p = (int32_t)_float(val); else return false; break; case SQNFT_INT64: if (vt == OT_INTEGER) *(int64_t *)p = (int64_t)_integer(val); else if (vt == OT_FLOAT) *(int64_t *)p = (int64_t)_float(val); else return false; break; case SQNFT_BOOL: if (vt == OT_BOOL || vt == OT_INTEGER) *(bool *)p = _integer(val) != 0; else return false; break; } return true; } inline void GetMember(uint32_t idx, SQObjectPtr &val) const { uint32_t kind = idx & MEMBER_KIND_MASK; if (kind == MEMBER_TYPE_FIELD) val = _realval(_values[_member_idxi(idx)]); else if (kind == MEMBER_TYPE_NATIVE_FIELD) GetNativeField(_member_idxi(idx), val); else val = _class->_methods[_member_idxi(idx)].val; } bool Get(const SQObjectPtr &key,SQObjectPtr &val) const { if(_class->_members->Get(key,val)) { GetMember(_integer(val), val); return true; } return false; } void SetMemberField(uint32_t idx, const SQObjectPtr &val) { _values[_member_idxi(idx)] = val; } SQInteger Set(const SQObjectPtr &key,const SQObjectPtr &val) { SQObjectPtr idx; if(_class->_members->Get(key,idx)) { if (_isfield(idx)) { SetMemberField(_integer(idx), val); return SLOT_STATUS_OK; } if (_isnativefield(idx)) { return SetNativeField(_member_idx(idx), val) ? SLOT_STATUS_OK : SLOT_STATUS_ERROR; } } return SLOT_STATUS_NO_MATCH; } void Release() { _uiRef++; if (_hook) { _hook(_thread(_sharedstate->_root_vm),_userpointer,0);} _uiRef--; if(_uiRef > 0) return; SQInteger size = _memsize; SQAllocContext ctx = _alloc_ctx; this->~SQInstance(); SQ_FREE(ctx, this, size); } void Finalize(); #ifndef NO_GARBAGE_COLLECTOR void Mark(SQCollectable ** ); SQObjectType GetType() {return OT_INSTANCE;} #endif bool InstanceOf(const SQClass *trg) const; bool GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res); SQClass *_class; SQUserPointer _userpointer; SQRELEASEHOOK _hook; SQAllocContext _alloc_ctx; SQInteger _memsize; SQObjectPtr _values[1]; }; #endif //_SQCLASS_H_ ================================================ FILE: squirrel/sqclosure.h ================================================ /* see copyright notice in squirrel.h */ #ifndef _SQCLOSURE_H_ #define _SQCLOSURE_H_ #define _CALC_CLOSURE_SIZE(func) (sizeof(SQClosure) + (func->_noutervalues*sizeof(SQObjectPtr)) + (func->_ndefaultparams*sizeof(SQObjectPtr))) struct SQFunctionProto; struct SQClass; struct SQClosure : public CHAINABLE_OBJ { private: SQClosure(SQSharedState *ss,SQFunctionProto *func) { _function = func; __ObjAddRef(_function); _base = NULL; INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); _env = NULL; } public: static SQClosure *Create(SQSharedState *ss,SQFunctionProto *func){ SQInteger size = _CALC_CLOSURE_SIZE(func); SQClosure *nc=(SQClosure*)SQ_MALLOC(ss->_alloc_ctx, size); new (nc) SQClosure(ss,func); nc->_outervalues = (SQObjectPtr *)(nc + 1); nc->_defaultparams = &nc->_outervalues[func->_noutervalues]; _CONSTRUCT_VECTOR(SQObjectPtr,func->_noutervalues,nc->_outervalues); _CONSTRUCT_VECTOR(SQObjectPtr,func->_ndefaultparams,nc->_defaultparams); return nc; } void Release(){ SQFunctionProto *f = _function; SQInteger size = _CALC_CLOSURE_SIZE(f); SQAllocContext ctx = f->_alloc_ctx; _DESTRUCT_VECTOR(SQObjectPtr,f->_noutervalues,_outervalues); _DESTRUCT_VECTOR(SQObjectPtr,f->_ndefaultparams,_defaultparams); __ObjRelease(_function); this->~SQClosure(); sq_vm_free(ctx, this, size); } SQClosure *Clone() { SQFunctionProto *f = _function; SQClosure * ret = SQClosure::Create(_opt_ss(this),f); ret->_env = _env; if(ret->_env) __ObjAddRef(ret->_env); _COPY_VECTOR(ret->_outervalues,_outervalues,f->_noutervalues); _COPY_VECTOR(ret->_defaultparams,_defaultparams,f->_ndefaultparams); return ret; } ~SQClosure(); bool Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write); static bool Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret); #ifndef NO_GARBAGE_COLLECTOR void Mark(SQCollectable **chain); void Finalize(){ SQFunctionProto *f = _function; _NULL_SQOBJECT_VECTOR(_outervalues,f->_noutervalues); _NULL_SQOBJECT_VECTOR(_defaultparams,f->_ndefaultparams); } SQObjectType GetType() {return OT_CLOSURE;} #endif SQWeakRef *_env; SQClass *_base; SQFunctionProto *_function; SQObjectPtr *_outervalues; SQObjectPtr *_defaultparams; }; ////////////////////////////////////////////// struct SQOuter : public CHAINABLE_OBJ { private: SQOuter(SQSharedState *ss, SQObjectPtr *outer){_valptr = outer; _next = NULL; INIT_CHAIN(); ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); } public: static SQOuter *Create(SQSharedState *ss, SQObjectPtr *outer) { SQOuter *nc = (SQOuter*)SQ_MALLOC(ss->_alloc_ctx, sizeof(SQOuter)); new (nc) SQOuter(ss, outer); return nc; } ~SQOuter() { REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); } void Release() { this->~SQOuter(); sq_vm_free(_sharedstate->_alloc_ctx, this, sizeof(SQOuter)); } #ifndef NO_GARBAGE_COLLECTOR void Mark(SQCollectable **chain); void Finalize() { _value.Null(); } SQObjectType GetType() {return OT_OUTER;} #endif SQObjectPtr *_valptr; /* pointer to value on stack, or _value below */ SQInteger _idx; /* idx in stack array, for relocation */ SQObjectPtr _value; /* value of outer after stack frame is closed */ SQOuter *_next; /* pointer to next outer when frame is open */ }; ////////////////////////////////////////////// struct SQGenerator : public CHAINABLE_OBJ { enum SQGeneratorState{eRunning,eSuspended,eDead}; private: SQGenerator(SQSharedState *ss,SQClosure *closure) : _stack(ss->_alloc_ctx), _etraps(ss->_alloc_ctx) { _closure=closure;_state=eRunning;_ci._generator=NULL;INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); } public: static SQGenerator *Create(SQSharedState *ss,SQClosure *closure){ SQGenerator *nc=(SQGenerator*)SQ_MALLOC(ss->_alloc_ctx, sizeof(SQGenerator)); new (nc) SQGenerator(ss,closure); return nc; } ~SQGenerator() { REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); } void Kill(){ _state=eDead; _stack.resize(0); _closure.Null();} void Release(){ sq_delete(_sharedstate->_alloc_ctx, this,SQGenerator); } bool Yield(SQVM *v,SQInteger target); bool Resume(SQVM *v,SQObjectPtr &dest); #ifndef NO_GARBAGE_COLLECTOR void Mark(SQCollectable **chain); void Finalize(){_stack.resize(0);_closure.Null();} SQObjectType GetType() {return OT_GENERATOR;} #endif SQObjectPtr _closure; SQObjectPtrVec _stack; SQVM::CallInfo _ci; ExceptionsTraps _etraps; SQGeneratorState _state; }; #define _CALC_NATVIVECLOSURE_SIZE(noutervalues) (sizeof(SQNativeClosure) + ((noutervalues)*sizeof(SQObjectPtr))) struct SQNativeClosure : public CHAINABLE_OBJ { private: SQNativeClosure(SQSharedState *ss,SQFUNCTION func) : _typecheck(ss->_alloc_ctx), _purefunction(false), _nodiscard(false) { _function=func;INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); _env = NULL; } public: static SQNativeClosure *Create(SQSharedState *ss,SQFUNCTION func,SQInteger nouters) { SQInteger size = _CALC_NATVIVECLOSURE_SIZE(nouters); SQNativeClosure *nc=(SQNativeClosure*)SQ_MALLOC(ss->_alloc_ctx, size); new (nc) SQNativeClosure(ss,func); nc->_outervalues = (SQObjectPtr *)(nc + 1); nc->_noutervalues = nouters; nc->_result_type_mask = ~0u; nc->_purefunction = false; nc->_nodiscard = false; _CONSTRUCT_VECTOR(SQObjectPtr,nc->_noutervalues,nc->_outervalues); return nc; } SQNativeClosure *Clone() { SQNativeClosure * ret = SQNativeClosure::Create(_opt_ss(this),_function,_noutervalues); ret->_env = _env; if(ret->_env) __ObjAddRef(ret->_env); ret->_name = _name; _COPY_VECTOR(ret->_outervalues,_outervalues,_noutervalues); ret->_result_type_mask = _result_type_mask; ret->_typecheck.copy(_typecheck); ret->_nparamscheck = _nparamscheck; ret->_purefunction = _purefunction; ret->_nodiscard = _nodiscard; return ret; } ~SQNativeClosure() { __ObjRelease(_env); REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); } void Release(){ SQInteger size = _CALC_NATVIVECLOSURE_SIZE(_noutervalues); _DESTRUCT_VECTOR(SQObjectPtr,_noutervalues,_outervalues); SQAllocContext ctx = _typecheck._alloc_ctx; this->~SQNativeClosure(); sq_free(ctx, this, size); } #ifndef NO_GARBAGE_COLLECTOR void Mark(SQCollectable **chain); void Finalize() { _NULL_SQOBJECT_VECTOR(_outervalues,_noutervalues); } SQObjectType GetType() {return OT_NATIVECLOSURE;} #endif SQInteger _nparamscheck; SQUnsignedInteger32 _noutervalues; SQUnsignedInteger32 _result_type_mask; bool _purefunction; bool _nodiscard; SQIntVec _typecheck; SQObjectPtr *_outervalues; SQWeakRef *_env; SQFUNCTION _function; SQObjectPtr _name; }; #endif //_SQCLOSURE_H_ ================================================ FILE: squirrel/sqdebug.cpp ================================================ /* see copyright notice in squirrel.h */ #include "sqpcheader.h" #include #include "sqvm.h" #include "sqfuncproto.h" #include "sqclosure.h" #include "sqstring.h" SQRESULT sq_getfunctioninfo(HSQUIRRELVM v,SQInteger level,SQFunctionInfo *fi) { SQInteger cssize = v->_callsstacksize; if (level >= 0 && cssize > level) { SQVM::CallInfo &ci = v->_callsstack[cssize-level-1]; if(sq_isclosure(ci._closure)) { SQClosure *c = _closure(ci._closure); SQFunctionProto *proto = c->_function; fi->funcid = proto; fi->name = sq_type(proto->_name) == OT_STRING?_stringval(proto->_name):"unknown"; fi->source = sq_type(proto->_sourcename) == OT_STRING?_stringval(proto->_sourcename):"unknown"; fi->line = proto->_lineinfos->_first_line; return SQ_OK; } } return sq_throwerror(v,"the object is not a closure"); } SQRESULT sq_stackinfos(HSQUIRRELVM v, SQInteger level, SQStackInfos *si) { SQInteger cssize = v->_callsstacksize; if (level >= 0 && cssize > level) { memset(si, 0, sizeof(SQStackInfos)); SQVM::CallInfo &ci = v->_callsstack[cssize-level-1]; switch (sq_type(ci._closure)) { case OT_CLOSURE:{ SQFunctionProto *func = _closure(ci._closure)->_function; if (sq_type(func->_name) == OT_STRING) si->funcname = _stringval(func->_name); if (sq_type(func->_sourcename) == OT_STRING) si->source = _stringval(func->_sourcename); si->line = func->GetLine(ci._ip); break; } case OT_NATIVECLOSURE: si->source = "NATIVE"; si->funcname = "unknown"; if(sq_type(_nativeclosure(ci._closure)->_name) == OT_STRING) si->funcname = _stringval(_nativeclosure(ci._closure)->_name); si->line = -1; break; default: break; //shutup compiler } return SQ_OK; } return SQ_ERROR; } void SQVM::Raise_Error(const char *s, ...) { va_list vl; va_start(vl, s); SQInteger buffersize = (SQInteger)strlen(s) + 256; vsnprintf(_sp(buffersize),buffersize, s, vl); va_end(vl); _lasterror = SQString::Create(_ss(this),_spval,-1); } void SQVM::Raise_Error(const SQObjectPtr &desc) { _lasterror = desc; } SQString *SQVM::PrintObjVal(const SQObject &o) { constexpr SQInteger MAX_STR_DISPLAY = 58; // Printable overhead: ' (1) + ...' (4) + ' (type='string')' (16) = 21 constexpr SQInteger MAX_REPR_LEN = MAX_STR_DISPLAY + 21; char buf[MAX_REPR_LEN + 1]; switch (sq_type(o)) { case OT_STRING: { SQInteger len = _string(o)->_len; if (len > MAX_STR_DISPLAY) { scsprintf(buf, sizeof(buf), "'%.*s...' (type='%s')", (int)MAX_STR_DISPLAY, _stringval(o), GetTypeName(o)); } else { scsprintf(buf, sizeof(buf), "'%s' (type='%s')", _stringval(o), GetTypeName(o)); } return SQString::Create(_ss(this), buf); } case OT_INTEGER: scsprintf(buf, sizeof(buf), "'" _PRINT_INT_FMT "' (type='%s')", _integer(o), GetTypeName(o)); return SQString::Create(_ss(this), buf); case OT_FLOAT: scsprintf(buf, sizeof(buf), "'%.14g' (type='%s')", _float(o), GetTypeName(o)); return SQString::Create(_ss(this), buf); default: return SQString::Create(_ss(this), GetTypeName(o)); } } void SQVM::Raise_IdxError(const SQObjectPtr &o) { SQObjectPtr oval(PrintObjVal(o)); Raise_Error("the index %s does not exist", _stringval(oval)); } void SQVM::Raise_MetamethodError(const char *mmname) { if (sq_type(_lasterror) == OT_STRING) { Raise_Error("Error in '%s' metamethod: %s", mmname, _stringval(_lasterror)); } else { SQObjectPtr oval(PrintObjVal(_lasterror)); Raise_Error("Error in '%s' metamethod: %s", mmname, _stringval(oval)); } } void SQVM::Raise_CompareError(const SQObject &o1, const SQObject &o2) { SQObjectPtr oval1(PrintObjVal(o1)), oval2(PrintObjVal(o2)); Raise_Error("comparison between %s and %s", _stringval(oval1), _stringval(oval2)); } void SQVM::Raise_ParamTypeError(SQInteger nparam,SQInteger typemask,SQInteger type,const char *funcname) { SQObjectPtr exptypes(SQString::Create(_ss(this), "", -1)); SQInteger found = 0; for(SQInteger i=0; i<16; i++) { SQInteger mask = ((SQInteger)1) << i; if(typemask & (mask)) { if(found>0) StringCat(exptypes, SQObjectPtr(SQString::Create(_ss(this), "|", -1)), exptypes); found ++; StringCat(exptypes, SQObjectPtr(SQString::Create(_ss(this), IdType2Name((SQObjectType)mask), -1)), exptypes); } } Raise_Error("parameter %d of '%s' has an invalid type '%s' ; expected: '%s'", (int)nparam, funcname && *funcname ? funcname : "", IdType2Name((SQObjectType)type), _stringval(exptypes)); } ================================================ FILE: squirrel/sqdedupshrinker.cpp ================================================ #include "sqpcheader.h" #include "sqstate.h" #include "sqvm.h" #include "sqtable.h" #include "sqarray.h" #define SHRINK_HASH_INIT 0x811c9dc5 #define SHRINK_HASH_MUL 0x01193193 static inline uint32_t calc_shrinker_hash(uint32_t * data, int bytes) { uint32_t hash = SHRINK_HASH_INIT; for (int i = 0; i < bytes / 4; i++) hash = (hash ^ data[i]) * SHRINK_HASH_MUL; return (hash >> 19) ^ hash; } struct SQDeduplicateShrinker { private: static constexpr int CACHE_SIZE = 1 << 12; // must be power of 2 static constexpr int CACHE_ITEMS_LIMIT = 20; SQObjectPtrVec arrayCache; SQObjectPtrVec tableCache; sqvector visitedTables; sqvector visitedArrays; public: SQDeduplicateShrinker(HSQUIRRELVM vm) : arrayCache(vm->_sharedstate->_alloc_ctx), tableCache(vm->_sharedstate->_alloc_ctx), visitedTables(vm->_sharedstate->_alloc_ctx), visitedArrays(vm->_sharedstate->_alloc_ctx) { arrayCache.resize(CACHE_SIZE); tableCache.resize(CACHE_SIZE); } SQDeduplicateShrinker(const SQDeduplicateShrinker &) = delete; SQDeduplicateShrinker(SQDeduplicateShrinker &&) = delete; SQDeduplicateShrinker & operator=(const SQDeduplicateShrinker &) = delete; SQDeduplicateShrinker & operator=(SQDeduplicateShrinker &&) = delete; bool shrink(SQObjectPtr & obj) { switch (obj._type) { case OT_TABLE: { obj._flags |= SQOBJ_FLAG_IMMUTABLE; bool simpleTypes = true; SQTable *t = _table(obj); int itemsCount = t->CountUsed(); SQTable::_HashNode *nodes = t->_nodes; int allocatedNodes = t->AllocatedNodes(); int cacheIndex = 0; if (itemsCount <= CACHE_ITEMS_LIMIT) { uint32_t hash = calc_shrinker_hash((uint32_t *)nodes, allocatedNodes * sizeof(SQTable::_HashNode)); cacheIndex = hash & (CACHE_SIZE - 1); if (sq_istable(tableCache[cacheIndex]) && t->IsBinaryEqual(_table(tableCache[cacheIndex]))) { obj = tableCache[cacheIndex]; return true; } } for (void *p : visitedTables) if (p == _table(obj)) return false; visitedTables.push_back(_table(obj)); for (int i = 0; i < allocatedNodes; i++) { if (!(sq_type(nodes[i].key) & OT_FREE_TABLE_SLOT)) { if (!sq_isstring(nodes[i].key) || (!sq_isnull(nodes[i].val) && !sq_isstring(nodes[i].val) && !sq_isinteger(nodes[i].val) && !sq_isfloat(nodes[i].val) && !sq_isbool(nodes[i].val))) simpleTypes = false; if (sq_isarray(nodes[i].val) || sq_istable(nodes[i].val)) shrink(nodes[i].val); // note: classType is not changed } } visitedTables.pop_back(); if (simpleTypes && itemsCount <= CACHE_ITEMS_LIMIT) tableCache[cacheIndex] = obj; } break; case OT_ARRAY: { bool simpleTypes = true; SQArray *array = _array(obj); array->ShrinkIfNeeded(); obj._flags |= SQOBJ_FLAG_IMMUTABLE; int cacheIndex = 0; if (array->Size() <= CACHE_ITEMS_LIMIT) { uint32_t hash = calc_shrinker_hash((uint32_t *)&array->_values[0], array->Size() * sizeof(SQObjectPtr)); cacheIndex = hash & (CACHE_SIZE - 1); if (sq_isarray(arrayCache[cacheIndex]) && array->IsBinaryEqual(_array(arrayCache[cacheIndex]))) { obj = arrayCache[cacheIndex]; return true; } } for (void *p : visitedArrays) if (p == _array(obj)) return false; visitedArrays.push_back(_array(obj)); for (int i = 0; i < int(array->Size()); i++) { if (!sq_isnull(array->_values[i]) && !sq_isstring(array->_values[i]) && !sq_isinteger(array->_values[i]) && !sq_isfloat(array->_values[i]) && !sq_isbool(array->_values[i])) simpleTypes = false; if (sq_isarray(array->_values[i]) || sq_istable(array->_values[i])) shrink(array->_values[i]); } visitedArrays.pop_back(); if (simpleTypes && array->Size() <= CACHE_ITEMS_LIMIT) arrayCache[cacheIndex] = obj; } default: break; } return false; } }; SQRESULT sq_deduplicate_object(HSQUIRRELVM vm, int index) { SQDeduplicateShrinker shrinker(vm); shrinker.shrink(stack_get(vm, index)); return SQ_OK; } ================================================ FILE: squirrel/sqext.cpp ================================================ #include "sqpcheader.h" #include "sqvm.h" #include "sqstate.h" #include "sqfuncproto.h" #include "sqclosure.h" #include "sqstring.h" #include "sqarray.h" SQRESULT sq_ext_getfuncinfo(HSQOBJECT obj, SQFunctionInfo *fi) { if (!fi) return SQ_ERROR; if (!sq_isclosure(obj) && !sq_isnativeclosure(obj)) return SQ_ERROR; if (sq_isclosure(obj)) { SQFunctionProto *proto = _closure(obj)->_function; if (!proto) return SQ_ERROR; fi->funcid = proto; fi->name = sq_type(proto->_name) == OT_STRING?_stringval(proto->_name):"unknown"; fi->source = sq_type(proto->_sourcename) == OT_STRING?_stringval(proto->_sourcename):"unknown"; fi->line = proto->_lineinfos->_first_line; } else { SQNativeClosure *c = _nativeclosure(obj); fi->funcid = (SQUserPointer)c->_function; fi->name = sq_type(c->_name) == OT_STRING?_stringval(c->_name):"unknown"; fi->source = "native"; fi->line = -1; } return SQ_OK; } SQRESULT sq_ext_get_array_floats(HSQOBJECT obj, int start, int count, float * dest) { if (sq_type(obj) != OT_ARRAY || !dest) return SQ_ERROR; SQArray * array = _array(obj); SQObjectPtrVec & values = array->_values; int arraySize = values.size(); if (count + start > arraySize) return SQ_ERROR; for (int i = 0; i < count; i++) { SQObjectPtr & v = values[i + start]; if (sq_type(v) == OT_FLOAT) dest[i] = _float(v); else if (sq_type(v) == OT_INTEGER || sq_type(v) == OT_BOOL) dest[i] = _integer(v); else dest[i] = 0; } return SQ_OK; } int sq_ext_get_array_int(HSQOBJECT obj, int index, int def) { if (sq_type(obj) != OT_ARRAY) return def; SQObjectPtrVec & values = _array(obj)->_values; if (index < 0 || index >= values.size()) return def; SQObjectPtr & v = values[index]; if (sq_type(v) == OT_INTEGER || sq_type(v) == OT_BOOL) return _integer(v); else if (sq_type(v) == OT_FLOAT) return int(_float(v)); else return def; } float sq_ext_get_array_float(HSQOBJECT obj, int index, float def) { if (sq_type(obj) != OT_ARRAY) return def; SQObjectPtrVec & values = _array(obj)->_values; if (index < 0 || index >= values.size()) return def; SQObjectPtr & v = values[index]; if (sq_type(v) == OT_FLOAT) return _float(v); if (sq_type(v) == OT_INTEGER || sq_type(v) == OT_BOOL) return _integer(v); else return def; } ================================================ FILE: squirrel/sqfuncproto.h ================================================ /* see copyright notice in squirrel.h */ #ifndef _SQFUNCTION_H_ #define _SQFUNCTION_H_ #include "opcodes.h" enum SQOuterType { otLOCAL = 0, otOUTER = 1 }; enum SQLangFeature { // parsing stage LF_FORBID_ROOT_TABLE = 0x000001, LF_FORBID_DELETE_OP = 0x000004, LF_FORBID_CLONE_OP = 0x000008, LF_FORBID_SWITCH_STMT = 0x000010, // code generation stage LF_DISABLE_OPTIMIZER = 0x000200, LF_FORBID_GLOBAL_CONST_REWRITE = 0x000400, LF_FORBID_IMPLICIT_TYPE_METHODS = 0x000800, LF_ALLOW_AUTO_FREEZE = 0x001000, LF_ALLOW_COMPILER_INTERNALS = 0x002000, LF_STRICT = LF_FORBID_ROOT_TABLE | LF_FORBID_DELETE_OP }; enum SQVarFlags { VF_ASSIGNABLE = 1 << 0, VF_INIT_WITH_CONST = 1 << 1, VF_PARAM = 1 << 2, VF_DESTRUCTURED = 1 << 3, VF_INIT_WITH_FREEZE = 1 << 4, VF_INIT_WITH_PURE = 1 << 5, VF_FIRST_LEVEL = 1 << 6, }; struct SQOuterVar { SQOuterVar() : _varFlags(VF_ASSIGNABLE) {} SQOuterVar(const SQObjectPtr &name,const SQObjectPtr &src,SQOuterType t,char varFlags) { _name = name; _src=src; _type=t; _varFlags=varFlags; } SQOuterVar(const SQOuterVar &ov) { _type=ov._type; _src=ov._src; _name=ov._name; _varFlags=ov._varFlags; } SQOuterType _type; char _varFlags; SQObjectPtr _name; SQObjectPtr _src; }; struct SQLocalVarInfo { SQLocalVarInfo():_start_op(0),_end_op(0),_pos(0),_varFlags(VF_ASSIGNABLE){} SQLocalVarInfo(const SQLocalVarInfo &lvi) { _name=lvi._name; _start_op=lvi._start_op; _end_op=lvi._end_op; _pos=lvi._pos; _varFlags=lvi._varFlags; } SQObjectPtr _name; uint32_t _start_op; uint32_t _end_op; uint32_t _pos; char _varFlags; }; struct SQLineInfosHeader { unsigned _first_line: 31; unsigned _is_compressed: 1; }; struct SQFullLineInfo { uint32_t _op; uint32_t _line_offset: 31; uint32_t _is_dbg_step_point: 1; }; struct SQCompressedLineInfo { uint8_t _op; uint8_t _line_offset: 7; uint8_t _is_dbg_step_point: 1; }; typedef sqvector SQOuterVarVec; typedef sqvector SQLocalVarInfoVec; typedef sqvector SQFullLineInfoVec; #define SQ_ALIGN_TO(n, t) (((n) + alignof(t) - 1) & ~(alignof(t) - 1)) #define _FUNC_SIZE(ni,nl,nparams,nfuncs,nouters,nlineinf,compressed,localinf,defparams,nstaticmemos) (sizeof(SQFunctionProto) \ +SQ_ALIGN_TO(((ni)-1)*sizeof(SQInstruction), SQObjectPtr)+((nl)*sizeof(SQObjectPtr)) \ +((nparams)*sizeof(SQObjectPtr))+((nfuncs)*sizeof(SQObjectPtr)) \ +((nouters)*sizeof(SQOuterVar)) \ +SQ_ALIGN_TO(sizeof(SQLineInfosHeader)+(nlineinf)*(compressed ? sizeof(SQCompressedLineInfo) : sizeof(SQFullLineInfo)), SQLocalVarInfo) \ +((localinf)*sizeof(SQLocalVarInfo))+((defparams)*sizeof(SQInt32))+((nparams)*sizeof(SQUnsignedInteger32)) \ +((nstaticmemos)*sizeof(SQObjectPtr))) struct SQFunctionProto : public CHAINABLE_OBJ { private: SQFunctionProto(SQSharedState *ss); ~SQFunctionProto(); public: static SQFunctionProto *Create(SQSharedState *ss, SQUnsignedInteger lang_features, SQInteger ninstructions, SQInteger nliterals,SQInteger nparameters, SQInteger nfunctions,SQInteger noutervalues, SQInteger nlineinfos, bool compressedLineInfos, SQInteger nlocalvarinfos, SQInteger ndefaultparams,SQInteger nstaticmemos ) { SQFunctionProto *f; //I compact the whole class and members in a single memory allocation size_t fnSize = _FUNC_SIZE(ninstructions,nliterals,nparameters,nfunctions,noutervalues,nlineinfos,compressedLineInfos,nlocalvarinfos,ndefaultparams,nstaticmemos); f = (SQFunctionProto *)sq_vm_malloc(ss->_alloc_ctx, fnSize); new (f) SQFunctionProto(ss); char *ptr = (char *)f->_instructions; f->_result_type_mask = ~0u; f->_alloc_ctx = ss->_alloc_ctx; f->lang_features = lang_features; f->_ninstructions = ninstructions; ptr += SQ_ALIGN_TO(ninstructions * sizeof(SQInstruction), SQLocalVarInfo); assert(size_t(ptr) % alignof(SQObjectPtr) == 0); f->_literals = (SQObjectPtr*)ptr; f->_nliterals = nliterals; ptr += nliterals * sizeof(SQObjectPtr); f->_parameters = (SQObjectPtr*)ptr; f->_nparameters = nparameters; ptr += nparameters * sizeof(SQObjectPtr); f->_functions = (SQObjectPtr*)ptr; f->_nfunctions = nfunctions; ptr += nfunctions * sizeof(SQObjectPtr); f->_staticmemos = (SQObjectPtr*)ptr; f->_nstaticmemos = nstaticmemos; ptr += nstaticmemos * sizeof(SQObjectPtr); assert(size_t(ptr) % alignof(SQOuterVar) == 0); f->_outervalues = (SQOuterVar*)ptr; f->_noutervalues = noutervalues; ptr += noutervalues * sizeof(SQOuterVar); assert(size_t(ptr) % alignof(SQLineInfosHeader) == 0); f->_lineinfos = (SQLineInfosHeader *)ptr; f->_nlineinfos = nlineinfos; ptr += SQ_ALIGN_TO(sizeof(SQLineInfosHeader) + nlineinfos * (compressedLineInfos ? sizeof(SQCompressedLineInfo) : sizeof(SQFullLineInfo)), SQLocalVarInfo); assert(size_t(ptr) % alignof(SQLocalVarInfo) == 0); f->_localvarinfos = (SQLocalVarInfo *)ptr; f->_nlocalvarinfos = nlocalvarinfos; ptr += nlocalvarinfos * sizeof(SQLocalVarInfo); assert(size_t(ptr) % alignof(SQInt32) == 0); f->_defaultparams = (SQInt32 *)ptr; f->_ndefaultparams = ndefaultparams; ptr += ndefaultparams * sizeof(SQInt32); f->_param_type_masks = (SQUnsignedInteger32 *)ptr; ptr += nparameters * sizeof(SQUnsignedInteger32); assert(ptr - (char *)f == fnSize); _CONSTRUCT_VECTOR(SQObjectPtr,f->_nliterals,f->_literals); _CONSTRUCT_VECTOR(SQObjectPtr,f->_nparameters,f->_parameters); _CONSTRUCT_VECTOR(SQObjectPtr,f->_nfunctions,f->_functions); _CONSTRUCT_VECTOR(SQObjectPtr,f->_nstaticmemos,f->_staticmemos); _CONSTRUCT_VECTOR(SQOuterVar,f->_noutervalues,f->_outervalues); //_CONSTRUCT_VECTOR(SQLineInfo,f->_nlineinfos,f->_lineinfos); //not required are 2 integers _CONSTRUCT_VECTOR(SQLocalVarInfo,f->_nlocalvarinfos,f->_localvarinfos); return f; } void Release(){ _DESTRUCT_VECTOR(SQObjectPtr,_nliterals,_literals); _DESTRUCT_VECTOR(SQObjectPtr,_nparameters,_parameters); _DESTRUCT_VECTOR(SQObjectPtr,_nfunctions,_functions); _DESTRUCT_VECTOR(SQObjectPtr,_nstaticmemos,_staticmemos); _DESTRUCT_VECTOR(SQOuterVar,_noutervalues,_outervalues); //_DESTRUCT_VECTOR(SQLineInfo,_nlineinfos,_lineinfos); //not required are 2 integers _DESTRUCT_VECTOR(SQLocalVarInfo,_nlocalvarinfos,_localvarinfos); SQInteger size = _FUNC_SIZE(_ninstructions,_nliterals,_nparameters,_nfunctions,_noutervalues,_nlineinfos,_lineinfos->_is_compressed,_nlocalvarinfos,_ndefaultparams,_nstaticmemos); SQAllocContext ctx = _alloc_ctx; this->~SQFunctionProto(); sq_vm_free(ctx, this, size); } const char* GetLocal(SQVM *v,SQUnsignedInteger stackbase,SQUnsignedInteger nseq,SQUnsignedInteger nop); static SQInteger GetLine(SQLineInfosHeader *lineinfos, int nlineinfos, int instruction_index, int *hint, bool *is_dbg_step_point = nullptr); SQInteger GetLine(const SQInstruction *curr, int *hint = nullptr, bool *is_dbg_step_point = nullptr); bool Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write); static bool Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret); #ifndef NO_GARBAGE_COLLECTOR void Mark(SQCollectable **chain); void Finalize(){ _NULL_SQOBJECT_VECTOR(_literals,_nliterals); _NULL_SQOBJECT_VECTOR(_staticmemos,_nstaticmemos); } SQObjectType GetType() {return OT_FUNCPROTO;} #endif SQAllocContext _alloc_ctx; SQObjectPtr _sourcename; SQObjectPtr _name; SQUnsignedInteger32 lang_features; SQUnsignedInteger32 _result_type_mask; bool _inside_hoisted_scope; bool _bgenerator; bool _purefunction; bool _nodiscard; SQInt32 _stacksize; SQInt32 _varparams; // Struct field order optimized for 64-bit platform memory alignment // General principle: Group pointers (8-byte) together, then two integers (4-byte), // to minimize padding and cache misses SQInt32 _nlineinfos; SQLineInfosHeader *_lineinfos; SQObjectPtr* _staticmemos; SQInt32 _nstaticmemos; SQInt32 _nlocalvarinfos; SQLocalVarInfo *_localvarinfos; SQObjectPtr *_literals; SQInt32 _nliterals; SQInt32 _nfunctions; SQObjectPtr *_functions; SQObjectPtr* _parameters; SQInt32 _nparameters; SQInt32 _noutervalues; SQOuterVar *_outervalues; SQUnsignedInteger32* _param_type_masks; SQInt32* _defaultparams; SQInt32 _ndefaultparams; SQInt32 _ninstructions; alignas(8) SQInstruction _instructions[1]; }; void Dump(SQFunctionProto *func, int instruction_index = -1); void Dump(OutputStream *stream, SQFunctionProto *func, bool deep = false, int instruction_index = -1); void ResetStaticMemos(SQFunctionProto *func, SQSharedState *ss); #endif //_SQFUNCTION_H_ ================================================ FILE: squirrel/sqmem.cpp ================================================ /* see copyright notice in squirrel.h */ #include "sqpcheader.h" #ifndef SQ_EXCLUDE_DEFAULT_MEMFUNCTIONS void sq_vm_init_alloc_context(SQAllocContext *) {} void sq_vm_destroy_alloc_context(SQAllocContext *) {} void sq_vm_assign_to_alloc_context(SQAllocContext, HSQUIRRELVM) {} void *sq_vm_malloc(SQAllocContext SQ_UNUSED_ARG(ctx), SQUnsignedInteger size) { return malloc(size); } void *sq_vm_realloc(SQAllocContext SQ_UNUSED_ARG(ctx), void *p, SQUnsignedInteger SQ_UNUSED_ARG(oldsize), SQUnsignedInteger size) { return realloc(p, size); } void sq_vm_free(SQAllocContext SQ_UNUSED_ARG(ctx), void *p, SQUnsignedInteger SQ_UNUSED_ARG(size)) { free(p); } #endif ================================================ FILE: squirrel/sqobject.cpp ================================================ /* see copyright notice in squirrel.h */ #include "sqpcheader.h" #include "sqvm.h" #include "sqstring.h" #include "sqarray.h" #include "sqtable.h" #include "squserdata.h" #include "sqfuncproto.h" #include "sqclass.h" #include "sqclosure.h" const char *IdType2Name(SQObjectType type) { switch(_RAW_TYPE(type)) { case OT_NULL: // fallthrough case _RT_NULL:return "null"; case _RT_INTEGER:return "integer"; case _RT_FLOAT:return "float"; case _RT_BOOL:return "bool"; case _RT_STRING:return "string"; case _RT_TABLE:return "table"; case _RT_ARRAY:return "array"; case _RT_GENERATOR:return "generator"; case _RT_CLOSURE: case _RT_NATIVECLOSURE: return "function"; case _RT_USERDATA: case _RT_USERPOINTER: return "userdata"; case _RT_THREAD: return "thread"; case _RT_FUNCPROTO: return "function"; case _RT_CLASS: return "class"; case _RT_INSTANCE: return "instance"; case _RT_WEAKREF: return "weakref"; case _RT_OUTER: return "outer"; default: return NULL; } } const char *GetTypeName(const SQObject &obj1) { return IdType2Name(sq_type(obj1)); } SQObjectPtr::SQObjectPtr(SQVM *vm, const char *str, SQInteger len) : SQObjectPtr(SQString::Create(vm->_sharedstate, str, len)) { } SQString *SQString::Create(SQSharedState *ss,const char *s,SQInteger len) { SQString *str=ADD_STRING(ss,s,len); return str; } void SQString::Release() { REMOVE_STRING(_sharedstate,this); } SQInteger SQString::Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval) { SQInteger idx = (SQInteger)TranslateIndex(refpos); if (idx < _len) { outkey = (SQInteger)idx; outval = (SQInteger)((SQUnsignedInteger)_val[idx]); //return idx for the next iteration return ++idx; } //nothing to iterate anymore return -1; } SQWeakRef *SQRefCounted::GetWeakRef(SQAllocContext alloc_ctx, SQObjectType type, SQObjectFlags flags) { if(!_weakref) { sq_new(alloc_ctx, _weakref, SQWeakRef); _weakref->_alloc_ctx = alloc_ctx; _weakref->_obj._unVal.raw = 0; _weakref->_obj._type = type; _weakref->_obj._flags = flags; _weakref->_obj._unVal.pRefCounted = this; } return _weakref; } SQRefCounted::~SQRefCounted() { if(_weakref) { _weakref->_obj._type = OT_NULL; _weakref->_obj._unVal.raw = 0; } } void SQWeakRef::Release() { if(ISREFCOUNTED(_obj._type)) { _obj._unVal.pRefCounted->_weakref = NULL; } sq_delete(_alloc_ctx, this, SQWeakRef); } bool SQDelegable::GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res) { if(_delegate) { return _delegate->Get((*_ss(v)->_metamethodnames)[mm],res); } return false; } bool SQDelegable::SetDelegate(SQTable *mt) { SQTable *temp = mt; if(temp == this) return false; while (temp) { if (temp->_delegate == this) return false; //cycle detected temp = temp->_delegate; } if (mt) __ObjAddRef(mt); __ObjRelease(_delegate); _delegate = mt; return true; } bool SQGenerator::Yield(SQVM *v,SQInteger target) { if(_state==eSuspended) { v->Raise_Error("internal vm error, yielding dead generator"); return false;} if(_state==eDead) { v->Raise_Error("internal vm error, yielding a dead generator"); return false; } SQInteger size = v->_top-v->_stackbase; _stack.resize(size); SQObject _this = v->_stack[v->_stackbase]; _stack._vals[0] = ISREFCOUNTED(sq_type(_this)) ? SQObjectPtr(_refcounted(_this)->GetWeakRef(_ss(v)->_alloc_ctx, sq_type(_this), _this._flags)) : _this; for(SQInteger n =1; n_stack[v->_stackbase+n]; } for(SQInteger j =0; j < size; j++) { v->_stack[v->_stackbase+j].Null(); } _ci = *v->ci; _ci._generator=NULL; for(SQInteger i=0;i<_ci._etraps;i++) { _etraps.push_back(v->_etraps.top()); v->_etraps.pop_back(); // store relative stack base and size in case of resume to other _top SQExceptionTrap &et = _etraps.back(); et._stackbase -= v->_stackbase; et._stacksize -= v->_stackbase; } _state=eSuspended; return true; } bool SQGenerator::Resume(SQVM *v,SQObjectPtr &dest) { if(_state==eDead){ v->Raise_Error("resuming dead generator"); return false; } if(_state==eRunning){ v->Raise_Error("resuming active generator"); return false; } SQInteger size = _stack.size(); SQInteger target = &dest - &(v->_stack._vals[v->_stackbase]); assert(target>=0 && target<=255); SQInteger newbase = v->_top; if(!v->EnterFrame(v->_top, v->_top + size, false)) return false; v->ci->_generator = this; v->ci->_target = (SQInt32)target; v->ci->_closure = _ci._closure; v->ci->_ip = _ci._ip; v->ci->_literals = _ci._literals; v->ci->_ncalls = _ci._ncalls; v->ci->_etraps = _ci._etraps; v->ci->_root = _ci._root; for(SQInteger i=0;i<_ci._etraps;i++) { v->_etraps.push_back(_etraps.top()); _etraps.pop_back(); SQExceptionTrap &et = v->_etraps.back(); // restore absolute stack base and size et._stackbase += newbase; et._stacksize += newbase; } SQObject _this = _stack._vals[0]; v->_stack[v->_stackbase] = sq_type(_this) == OT_WEAKREF ? _weakref(_this)->_obj : _this; for(SQInteger n = 1; n_stack[v->_stackbase+n] = _stack._vals[n]; _stack._vals[n].Null(); } _state=eRunning; if (v->_debughook) v->CallDebugHook('c'); return true; } void SQArray::Extend(const SQArray *a){ SQInteger xlen; if((xlen=a->Size())) for(SQInteger i=0;i_values[i]); } bool SQArray::IsBinaryEqual(const SQArray *o) { if (this == o) return true; if (_values.size() != o->_values.size()) return false; if (_values.size() == 0) return true; return memcmp(_values._vals, o->_values._vals, _values.size() * sizeof(SQObjectPtr)) == 0; } const char* SQFunctionProto::GetLocal(SQVM *vm,SQUnsignedInteger stackbase,SQUnsignedInteger nseq,SQUnsignedInteger nop) { SQUnsignedInteger nvars=_nlocalvarinfos; const char *res=NULL; if(nvars>=nseq){ for(SQUnsignedInteger i=0;i=nop) { if(nseq==0){ vm->Push(vm->_stack[stackbase+_localvarinfos[i]._pos]); res=_stringval(_localvarinfos[i]._name); break; } nseq--; } } } return res; } template inline SQInteger get_line_offset_impl(T* lineinfos, int nlineinfos, int instruction_index, int* hint, bool* is_dbg_step_point) { int pos = nlineinfos - 1; int low = 0; int high = nlineinfos - 1; int tryCount = 20; if (hint && unsigned(*hint) < unsigned(high)) { int h = *hint; if (instruction_index >= (int)lineinfos[h]._op && instruction_index < (int)lineinfos[h + 1]._op) { if (is_dbg_step_point) *is_dbg_step_point = lineinfos[h]._is_dbg_step_point; return lineinfos[h]._line_offset; } else if (instruction_index >= (int)lineinfos[h + 1]._op && instruction_index < (int)lineinfos[h + 2]._op) { h++; *hint = h; if (is_dbg_step_point) *is_dbg_step_point = lineinfos[h]._is_dbg_step_point; return lineinfos[h]._line_offset; } else if (instruction_index == 0) { for (int i = 0; i < nlineinfos - 1; i++) if (instruction_index >= (int)lineinfos[i]._op && instruction_index < (int)lineinfos[i + 1]._op) { *hint = i; if (is_dbg_step_point) *is_dbg_step_point = lineinfos[i]._is_dbg_step_point; return lineinfos[i]._line_offset; } } } while (high >= low && --tryCount) { int mid = (high + low) / 2; if (instruction_index < (int)lineinfos[mid]._op) high = mid - 1; else if (instruction_index >= (int)lineinfos[mid + 1]._op) low = mid + 1; else { pos = mid; break; } } if (tryCount == 0) { // TODO: failsafe pass, to be reomved later assert(0); for (int i = 0; i < nlineinfos - 1; i++) if (instruction_index >= (int)lineinfos[i]._op && instruction_index < (int)lineinfos[i + 1]._op) { pos = i; break; } } if (is_dbg_step_point) *is_dbg_step_point = lineinfos[pos]._is_dbg_step_point; if (hint) *hint = pos; return lineinfos[pos]._line_offset; } SQInteger SQFunctionProto::GetLine(SQLineInfosHeader* lineinfos, int nlineinfos, int instruction_index, int* hint, bool* is_dbg_step_point) { if (lineinfos->_is_compressed) return get_line_offset_impl((SQCompressedLineInfo *)(void *)(lineinfos + 1), nlineinfos, instruction_index, hint, is_dbg_step_point) + lineinfos->_first_line; else return get_line_offset_impl((SQFullLineInfo *)(void *)(lineinfos + 1), nlineinfos, instruction_index, hint, is_dbg_step_point) + lineinfos->_first_line; } SQInteger SQFunctionProto::GetLine(const SQInstruction *curr, int *hint, bool *is_dbg_step_point) { return GetLine(_lineinfos, _nlineinfos, int(curr - _instructions), hint, is_dbg_step_point); } SQClosure::~SQClosure() { __ObjRelease(_env); __ObjRelease(_base); REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); } #define _CHECK_IO(exp) { if(!exp)return false; } static bool SafeWrite(HSQUIRRELVM v,SQWRITEFUNC write,SQUserPointer up,SQUserPointer dest,SQInteger size) { if(write(up,dest,size) != size) { v->Raise_Error("io error (write function failure)"); return false; } return true; } static bool SafeRead(HSQUIRRELVM v,SQREADFUNC read,SQUserPointer up,SQUserPointer dest,SQInteger size) { if(size && read(up,dest,size) != size) { v->Raise_Error("io error, read function failure, the origin stream could be corrupted/trucated"); return false; } return true; } static bool WriteTag(HSQUIRRELVM v,SQWRITEFUNC write,SQUserPointer up,SQUnsignedInteger32 tag) { return SafeWrite(v,write,up,&tag,sizeof(tag)); } static bool CheckTag(HSQUIRRELVM v,SQREADFUNC read,SQUserPointer up,SQUnsignedInteger32 tag) { SQUnsignedInteger32 t; _CHECK_IO(SafeRead(v,read,up,&t,sizeof(t))); if(t != tag){ v->Raise_Error("invalid or corrupted closure stream"); return false; } return true; } static bool WriteObject(HSQUIRRELVM v,SQUserPointer up,SQWRITEFUNC write,SQObjectPtr &o) { SQUnsignedInteger32 _type = (SQUnsignedInteger32)sq_type(o); _CHECK_IO(SafeWrite(v,write,up,&_type,sizeof(_type))); switch(sq_type(o)){ case OT_STRING: _CHECK_IO(SafeWrite(v,write,up,&_string(o)->_len,sizeof(SQInteger))); _CHECK_IO(SafeWrite(v,write,up,_stringval(o),_string(o)->_len)); break; case OT_BOOL: case OT_INTEGER: _CHECK_IO(SafeWrite(v,write,up,&_integer(o),sizeof(SQInteger)));break; case OT_FLOAT: _CHECK_IO(SafeWrite(v,write,up,&_float(o),sizeof(SQFloat)));break; case OT_NULL: break; default: v->Raise_Error("cannot serialize a %s",GetTypeName(o)); return false; } return true; } static bool ReadObject(HSQUIRRELVM v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &o) { SQUnsignedInteger32 _type; _CHECK_IO(SafeRead(v,read,up,&_type,sizeof(_type))); SQObjectType t = (SQObjectType)_type; switch(t){ case OT_STRING:{ SQInteger len; _CHECK_IO(SafeRead(v,read,up,&len,sizeof(SQInteger))); _CHECK_IO(SafeRead(v,read,up,_ss(v)->GetScratchPad(len),len)); o=SQString::Create(_ss(v),_ss(v)->GetScratchPad(-1),len); } break; case OT_INTEGER:{ SQInteger i; _CHECK_IO(SafeRead(v,read,up,&i,sizeof(SQInteger))); o = i; break; } case OT_BOOL:{ SQInteger i; _CHECK_IO(SafeRead(v,read,up,&i,sizeof(SQInteger))); o._type = OT_BOOL; o._unVal.nInteger = i; break; } case OT_FLOAT:{ SQFloat f; _CHECK_IO(SafeRead(v,read,up,&f,sizeof(SQFloat))); o = f; break; } case OT_NULL: o.Null(); break; default: v->Raise_Error("cannot serialize a %s",IdType2Name(t)); return false; } return true; } bool SQClosure::Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write) { _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_HEAD)); _CHECK_IO(WriteTag(v,write,up,sizeof(char))); _CHECK_IO(WriteTag(v,write,up,sizeof(SQInteger))); _CHECK_IO(WriteTag(v,write,up,sizeof(SQFloat))); _CHECK_IO(_function->Save(v,up,write)); _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_TAIL)); return true; } bool SQClosure::Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret) { _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_HEAD)); _CHECK_IO(CheckTag(v,read,up,sizeof(char))); _CHECK_IO(CheckTag(v,read,up,sizeof(SQInteger))); _CHECK_IO(CheckTag(v,read,up,sizeof(SQFloat))); SQObjectPtr func; _CHECK_IO(SQFunctionProto::Load(v,up,read,func)); _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_TAIL)); ret = SQClosure::Create(_ss(v),_funcproto(func)); return true; } SQFunctionProto::SQFunctionProto(SQSharedState *ss) { _stacksize=0; _bgenerator=false; _purefunction=false; _nodiscard=false; _inside_hoisted_scope=false; INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); } SQFunctionProto::~SQFunctionProto() { REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain,this); } bool SQFunctionProto::Save(SQVM *v,SQUserPointer up,SQWRITEFUNC write) { SQInteger i,nliterals = _nliterals,nparameters = _nparameters; SQInteger noutervalues = _noutervalues,nlocalvarinfos = _nlocalvarinfos; SQInteger nlineinfos=_nlineinfos,ninstructions = _ninstructions,nfunctions=_nfunctions; SQInteger ndefaultparams = _ndefaultparams; SQInteger nstaticmemos = _nstaticmemos; bool compressedLineInfos = _lineinfos->_is_compressed; _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART)); _CHECK_IO(WriteObject(v,up,write,_sourcename)); _CHECK_IO(WriteObject(v,up,write,_name)); _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART)); _CHECK_IO(SafeWrite(v,write,up, &lang_features, sizeof(lang_features))); _CHECK_IO(SafeWrite(v,write,up,&nliterals,sizeof(nliterals))); _CHECK_IO(SafeWrite(v,write,up,&nparameters,sizeof(nparameters))); _CHECK_IO(SafeWrite(v,write,up,&noutervalues,sizeof(noutervalues))); _CHECK_IO(SafeWrite(v,write,up,&nlocalvarinfos,sizeof(nlocalvarinfos))); _CHECK_IO(SafeWrite(v,write,up,&nlineinfos,sizeof(nlineinfos))); _CHECK_IO(SafeWrite(v,write,up,&compressedLineInfos,sizeof(compressedLineInfos))); _CHECK_IO(SafeWrite(v,write,up,&ndefaultparams,sizeof(ndefaultparams))); _CHECK_IO(SafeWrite(v,write,up,&ninstructions,sizeof(ninstructions))); _CHECK_IO(SafeWrite(v,write,up,&nfunctions,sizeof(nfunctions))); _CHECK_IO(SafeWrite(v,write,up,&nstaticmemos,sizeof(nstaticmemos))); _CHECK_IO(WriteTag(v,write,up,SQ_CLOSURESTREAM_PART)); for(i=0;iSave(v,up,write)); } _CHECK_IO(SafeWrite(v,write,up,&_stacksize,sizeof(_stacksize))); _CHECK_IO(SafeWrite(v,write,up,&_bgenerator,sizeof(_bgenerator))); _CHECK_IO(SafeWrite(v,write,up,&_purefunction,sizeof(_purefunction))); _CHECK_IO(SafeWrite(v,write,up,&_nodiscard,sizeof(_nodiscard))); _CHECK_IO(SafeWrite(v,write,up,&_varparams,sizeof(_varparams))); return true; } bool SQFunctionProto::Load(SQVM *v,SQUserPointer up,SQREADFUNC read,SQObjectPtr &ret) { SQInteger i, nliterals,nparameters; SQUnsignedInteger langFeatures; SQInteger noutervalues ,nlocalvarinfos ; SQInteger nlineinfos,ninstructions ,nfunctions,ndefaultparams ; SQInteger nstaticmemos; SQObjectPtr sourcename, name; SQObjectPtr o; bool compressedLineInfos = false; _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); _CHECK_IO(ReadObject(v, up, read, sourcename)); _CHECK_IO(ReadObject(v, up, read, name)); _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); _CHECK_IO(SafeRead(v,read,up, &langFeatures, sizeof(langFeatures))); _CHECK_IO(SafeRead(v,read,up, &nliterals, sizeof(nliterals))); _CHECK_IO(SafeRead(v,read,up, &nparameters, sizeof(nparameters))); _CHECK_IO(SafeRead(v,read,up, &noutervalues, sizeof(noutervalues))); _CHECK_IO(SafeRead(v,read,up, &nlocalvarinfos, sizeof(nlocalvarinfos))); _CHECK_IO(SafeRead(v,read,up, &nlineinfos, sizeof(nlineinfos))); _CHECK_IO(SafeRead(v,read,up, &compressedLineInfos, sizeof(compressedLineInfos))); _CHECK_IO(SafeRead(v,read,up, &ndefaultparams, sizeof(ndefaultparams))); _CHECK_IO(SafeRead(v,read,up, &ninstructions, sizeof(ninstructions))); _CHECK_IO(SafeRead(v,read,up, &nfunctions, sizeof(nfunctions))); _CHECK_IO(SafeRead(v,read,up, &nstaticmemos, sizeof(nfunctions))); SQFunctionProto *f = SQFunctionProto::Create(_opt_ss(v), langFeatures, ninstructions,nliterals,nparameters, nfunctions,noutervalues,nlineinfos,compressedLineInfos, nlocalvarinfos,ndefaultparams,nstaticmemos); SQObjectPtr proto(f); //gets a ref in case of failure f->_sourcename = sourcename; f->_name = name; _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); for(i = 0;i < nliterals; i++){ _CHECK_IO(ReadObject(v, up, read, o)); f->_literals[i] = o; } _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); for(i = 0; i < nparameters; i++){ _CHECK_IO(ReadObject(v, up, read, o)); f->_parameters[i] = o; } _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); for(i = 0; i < noutervalues; i++){ SQUnsignedInteger type; SQObjectPtr name; SQInteger varFlags; _CHECK_IO(SafeRead(v,read,up, &type, sizeof(SQUnsignedInteger))); _CHECK_IO(ReadObject(v, up, read, o)); _CHECK_IO(ReadObject(v, up, read, name)); _CHECK_IO(SafeRead(v, read, up, &varFlags, sizeof(SQInteger))); f->_outervalues[i] = SQOuterVar(name,o, (SQOuterType)type, varFlags); } _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); for(i = 0; i < nlocalvarinfos; i++){ SQLocalVarInfo lvi; _CHECK_IO(ReadObject(v, up, read, lvi._name)); _CHECK_IO(SafeRead(v,read,up, &lvi._pos, sizeof(SQUnsignedInteger))); _CHECK_IO(SafeRead(v,read,up, &lvi._start_op, sizeof(SQUnsignedInteger))); _CHECK_IO(SafeRead(v,read,up, &lvi._end_op, sizeof(SQUnsignedInteger))); f->_localvarinfos[i] = lvi; } _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); size_t lineinfosSize = (char *)f->_defaultparams - (char *)f->_lineinfos; _CHECK_IO(SafeRead(v,read,up, f->_lineinfos, lineinfosSize)); _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); _CHECK_IO(SafeRead(v,read,up, f->_defaultparams, sizeof(SQInteger)*ndefaultparams)); _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); _CHECK_IO(SafeRead(v,read,up, f->_instructions, sizeof(SQInstruction)*ninstructions)); _CHECK_IO(CheckTag(v,read,up,SQ_CLOSURESTREAM_PART)); for(i = 0; i < nfunctions; i++){ _CHECK_IO(_funcproto(o)->Load(v, up, read, o)); f->_functions[i] = o; } _CHECK_IO(SafeRead(v,read,up, &f->_stacksize, sizeof(f->_stacksize))); _CHECK_IO(SafeRead(v,read,up, &f->_bgenerator, sizeof(f->_bgenerator))); _CHECK_IO(SafeRead(v,read,up, &f->_purefunction, sizeof(f->_purefunction))); _CHECK_IO(SafeRead(v,read,up, &f->_nodiscard, sizeof(f->_nodiscard))); _CHECK_IO(SafeRead(v,read,up, &f->_varparams, sizeof(f->_varparams))); ret = f; return true; } #ifndef NO_GARBAGE_COLLECTOR #define START_MARK() if(!(_uiRef&MARK_FLAG)){ \ _uiRef|=MARK_FLAG; #define END_MARK() RemoveFromChain(&_sharedstate->_gc_chain, this); \ AddToChain(chain, this); } void SQVM::Mark(SQCollectable **chain) { START_MARK() SQSharedState::MarkObject(_lasterror,chain); SQSharedState::MarkObject(_errorhandler,chain); SQSharedState::MarkObject(_debughook_closure,chain); SQSharedState::MarkObject(_roottable, chain); SQSharedState::MarkObject(temp_reg, chain); for(SQUnsignedInteger i = 0; i < _stack.size(); i++) SQSharedState::MarkObject(_stack[i], chain); for(SQInteger k = 0; k < _callsstacksize; k++) SQSharedState::MarkObject(_callsstack[k]._closure, chain); END_MARK() } void SQArray::Mark(SQCollectable **chain) { START_MARK() SQInteger len = _values.size(); for(SQInteger i = 0;i < len; i++) SQSharedState::MarkObject(_values[i], chain); END_MARK() } void SQTable::Mark(SQCollectable **chain) { START_MARK() if(_delegate) _delegate->Mark(chain); SQInteger len = _numofnodes_minus_one; for(SQInteger i = 0; i <= len; i++){ SQSharedState::MarkObject(_nodes[i].key, chain); SQSharedState::MarkObject(_nodes[i].val, chain); } END_MARK() } void SQClass::Mark(SQCollectable **chain) { START_MARK() _members->Mark(chain); if(_base) _base->Mark(chain); for(SQUnsignedInteger i =0; i< _defaultvalues.size(); i++) { SQSharedState::MarkObject(_defaultvalues[i].val, chain); } for(SQUnsignedInteger j =0; j< _methods.size(); j++) { SQSharedState::MarkObject(_methods[j].val, chain); } for(SQUnsignedInteger k =0; k< MT_NUM_METHODS; k++) { SQSharedState::MarkObject(_metamethods[k], chain); } END_MARK() } void SQInstance::Mark(SQCollectable **chain) { START_MARK() _class->Mark(chain); SQUnsignedInteger nvalues = _class->_defaultvalues.size(); for(SQUnsignedInteger i =0; i< nvalues; i++) { SQSharedState::MarkObject(_values[i], chain); } END_MARK() } void SQGenerator::Mark(SQCollectable **chain) { START_MARK() for(SQUnsignedInteger i = 0; i < _stack.size(); i++) SQSharedState::MarkObject(_stack[i], chain); SQSharedState::MarkObject(_closure, chain); END_MARK() } void SQFunctionProto::Mark(SQCollectable **chain) { START_MARK() for(SQInteger i = 0; i < _nliterals; i++) SQSharedState::MarkObject(_literals[i], chain); for(SQInteger k = 0; k < _nfunctions; k++) SQSharedState::MarkObject(_functions[k], chain); END_MARK() } void SQClosure::Mark(SQCollectable **chain) { START_MARK() if(_base) _base->Mark(chain); SQFunctionProto *fp = _function; fp->Mark(chain); for(SQInteger i = 0; i < fp->_noutervalues; i++) SQSharedState::MarkObject(_outervalues[i], chain); for(SQInteger k = 0; k < fp->_ndefaultparams; k++) SQSharedState::MarkObject(_defaultparams[k], chain); for(SQInteger j = 0; j < fp->_nstaticmemos; j++) SQSharedState::MarkObject(fp->_staticmemos[j], chain); END_MARK() } void SQNativeClosure::Mark(SQCollectable **chain) { START_MARK() for(SQUnsignedInteger i = 0; i < _noutervalues; i++) SQSharedState::MarkObject(_outervalues[i], chain); END_MARK() } void SQOuter::Mark(SQCollectable **chain) { START_MARK() /* If the valptr points to a closed value, that value is alive */ if(_valptr == &_value) { SQSharedState::MarkObject(_value, chain); } END_MARK() } void SQUserData::Mark(SQCollectable **chain){ START_MARK() if(_delegate) _delegate->Mark(chain); END_MARK() } void SQCollectable::UnMark() { _uiRef&=~MARK_FLAG; } #endif ================================================ FILE: squirrel/sqobject.h ================================================ /* see copyright notice in squirrel.h */ #ifndef _SQOBJECT_H_ #define _SQOBJECT_H_ #include "squtils.h" #define UINT32_MINUS_ONE (0xFFFFFFFF) #define SQ_CLOSURESTREAM_HEAD (('S'<<24)|('Q'<<16)|('I'<<8)|('R')) #define SQ_CLOSURESTREAM_PART (('P'<<24)|('A'<<16)|('R'<<8)|('T')) #define SQ_CLOSURESTREAM_TAIL (('T'<<24)|('A'<<16)|('I'<<8)|('L')) struct SQSharedState; #define METAMETHODS_LIST \ MM_IMPL(MT_ADD ,"_add")\ MM_IMPL(MT_SUB ,"_sub")\ MM_IMPL(MT_MUL ,"_mul")\ MM_IMPL(MT_DIV ,"_div")\ MM_IMPL(MT_UNM ,"_unm")\ MM_IMPL(MT_MODULO ,"_modulo")\ MM_IMPL(MT_SET ,"_set")\ MM_IMPL(MT_GET ,"_get")\ MM_IMPL(MT_TYPEOF ,"_typeof")\ MM_IMPL(MT_NEXTI ,"_nexti")\ MM_IMPL(MT_CMP ,"_cmp")\ MM_IMPL(MT_CALL ,"_call")\ MM_IMPL(MT_CLONED ,"_cloned")\ MM_IMPL(MT_NEWSLOT ,"_newslot")\ MM_IMPL(MT_DELSLOT ,"_delslot")\ MM_IMPL(MT_TOSTRING ,"_tostring")\ MM_IMPL(MT_LOCK ,"_lock")\ #define MM_IMPL(mm, name) mm, enum SQMetaMethod{ METAMETHODS_LIST MT_NUM_METHODS }; #undef MM_IMPL #define _CONSTRUCT_VECTOR(type,size,ptr) { \ for(SQInteger n = 0; n < ((SQInteger)size); n++) { \ new (&ptr[n]) type(); \ } \ } #define _DESTRUCT_VECTOR(type,size,ptr) { \ for(SQInteger nl = 0; nl < ((SQInteger)size); nl++) { \ ptr[nl].~type(); \ } \ } #define _COPY_VECTOR(dest,src,size) { \ for(SQInteger _n_ = 0; _n_ < ((SQInteger)size); _n_++) { \ dest[_n_] = src[_n_]; \ } \ } #define _NULL_SQOBJECT_VECTOR(vec,size) { \ for(SQInteger _n_ = 0; _n_ < ((SQInteger)size); _n_++) { \ vec[_n_].Null(); \ } \ } struct SQRefCounted { SQUnsignedInteger _uiRef; struct SQWeakRef *_weakref; SQRefCounted() { _uiRef = 0; _weakref = NULL; } virtual ~SQRefCounted(); SQWeakRef *GetWeakRef(SQAllocContext alloc_ctx, SQObjectType type, SQObjectFlags flags); virtual void Release()=0; }; struct SQWeakRef : SQRefCounted { void Release(); SQObject _obj; SQAllocContext _alloc_ctx; }; #define _realval(o) (sq_type((o)) != OT_WEAKREF?(SQObject)o:_weakref(o)->_obj) struct SQObjectPtr; #define __AddRef(type,unval) if(ISREFCOUNTED(type)) \ { \ unval.pRefCounted->_uiRef++; \ } #define __Release(type,unval) if(ISREFCOUNTED(type) && ((--unval.pRefCounted->_uiRef)==0)) \ { \ unval.pRefCounted->Release(); \ } #define __ObjRelease(obj) { \ if((obj)) { \ (obj)->_uiRef--; \ if((obj)->_uiRef == 0) \ (obj)->Release(); \ (obj) = NULL; \ } \ } #define __ObjAddRef(obj) { \ (obj)->_uiRef++; \ } #define is_delegable(t) (sq_type(t)&SQOBJECT_DELEGABLE) #define raw_type(obj) _RAW_TYPE((obj)._type) #define _integer(obj) ((obj)._unVal.nInteger) #define _float(obj) ((obj)._unVal.fFloat) #define _string(obj) ((obj)._unVal.pString) #define _table(obj) ((obj)._unVal.pTable) #define _array(obj) ((obj)._unVal.pArray) #define _closure(obj) ((obj)._unVal.pClosure) #define _generator(obj) ((obj)._unVal.pGenerator) #define _nativeclosure(obj) ((obj)._unVal.pNativeClosure) #define _userdata(obj) ((obj)._unVal.pUserData) #define _userpointer(obj) ((obj)._unVal.pUserPointer) #define _thread(obj) ((obj)._unVal.pThread) #define _funcproto(obj) ((obj)._unVal.pFunctionProto) #define _class(obj) ((obj)._unVal.pClass) #define _instance(obj) ((obj)._unVal.pInstance) #define _delegable(obj) ((SQDelegable *)(obj)._unVal.pDelegable) #define _weakref(obj) ((obj)._unVal.pWeakRef) #define _outer(obj) ((obj)._unVal.pOuter) #define _refcounted(obj) ((obj)._unVal.pRefCounted) #define _rawval(obj) ((obj)._unVal.raw) #define _stringval(obj) (obj)._unVal.pString->_val #define _userdataval(obj) ((SQUserPointer)sq_aligning((obj)._unVal.pUserData + 1)) #define tofloat(num) ((sq_type(num)==OT_INTEGER)?(SQFloat)_integer(num):_float(num)) #define tointeger(num) ((sq_type(num)==OT_FLOAT)?(SQInteger)_float(num):_integer(num)) ///////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////////// #if defined(SQUSEDOUBLE) && !defined(_SQ64) || !defined(SQUSEDOUBLE) && defined(_SQ64) #define SQ_REFOBJECT_INIT() SQ_OBJECT_RAWINIT() #else #define SQ_REFOBJECT_INIT() #endif #define _REF_TYPE_DECL(type,_class,sym) \ explicit SQObjectPtr(_class * __restrict x) \ { \ SQ_OBJECT_RAWINIT() \ _type=type; \ _flags=0; \ _unVal.sym = x; \ assert(_unVal.pTable); \ _unVal.pRefCounted->_uiRef++; \ } \ inline SQObjectPtr& operator=(_class *__restrict x) \ { \ SQObjectType tOldType = _type; \ SQObjectValue unOldVal = _unVal; \ _type = type; \ _flags = 0; \ SQ_REFOBJECT_INIT() \ _unVal.sym = x; \ _unVal.pRefCounted->_uiRef++; \ __Release(tOldType,unOldVal); \ return *this; \ } #define _SCALAR_TYPE_DECL(type,_class,sym) \ explicit SQObjectPtr(_class x) \ { \ SQ_OBJECT_RAWINIT() \ _type=type; \ _flags = 0; \ _unVal.sym = x; \ } \ inline SQObjectPtr& operator=(_class x) \ { \ __Release(_type,_unVal); \ _type = type; \ _flags = 0; \ SQ_OBJECT_RAWINIT() \ _unVal.sym = x; \ return *this; \ } struct SQObjectPtr : public SQObject { SQObjectPtr() noexcept { memset(this, 0, sizeof(SQObjectPtr)); // OT_NULL == 0 } SQObjectPtr(const SQObjectPtr &__restrict o) { memcpy(this, &o, sizeof(o)); __AddRef(_type,_unVal); } SQObjectPtr(SQObjectPtr &&__restrict o) noexcept { memcpy(this, &o, sizeof(o)); memset(&o, 0, sizeof(SQObjectPtr)); // OT_NULL == 0 } explicit SQObjectPtr(const SQObject &__restrict o) { memcpy(this, &o, sizeof(o)); __AddRef(_type,_unVal); } _REF_TYPE_DECL(OT_TABLE,SQTable,pTable) _REF_TYPE_DECL(OT_CLASS,SQClass,pClass) _REF_TYPE_DECL(OT_INSTANCE,SQInstance,pInstance) _REF_TYPE_DECL(OT_ARRAY,SQArray,pArray) _REF_TYPE_DECL(OT_CLOSURE,SQClosure,pClosure) _REF_TYPE_DECL(OT_NATIVECLOSURE,SQNativeClosure,pNativeClosure) _REF_TYPE_DECL(OT_OUTER,SQOuter,pOuter) _REF_TYPE_DECL(OT_GENERATOR,SQGenerator,pGenerator) _REF_TYPE_DECL(OT_STRING,SQString,pString) _REF_TYPE_DECL(OT_USERDATA,SQUserData,pUserData) _REF_TYPE_DECL(OT_WEAKREF,SQWeakRef,pWeakRef) _REF_TYPE_DECL(OT_THREAD,SQVM,pThread) _REF_TYPE_DECL(OT_FUNCPROTO,SQFunctionProto,pFunctionProto) _SCALAR_TYPE_DECL(OT_INTEGER,SQInteger,nInteger) #ifdef _SQ64 _SCALAR_TYPE_DECL(OT_INTEGER,SQInt32,nInteger) #endif _SCALAR_TYPE_DECL(OT_FLOAT,SQFloat,fFloat) _SCALAR_TYPE_DECL(OT_USERPOINTER,SQUserPointer,pUserPointer) SQObjectPtr(SQVM *vm, const char *str, SQInteger len = -1); explicit SQObjectPtr(bool bBool) { memset(this, 0, sizeof(SQObjectPtr)); _type = OT_BOOL; if (bBool) _unVal.nInteger = 1; } inline SQObjectPtr& operator=(bool b) { __Release(_type,_unVal); SQ_OBJECT_RAWINIT() _type = OT_BOOL; _flags = 0; _unVal.nInteger = b?1:0; return *this; } ~SQObjectPtr() { __Release(_type,_unVal); } inline SQObjectPtr& operator=(const SQObjectPtr& __restrict obj) { SQObjectType tOldType = _type; SQObjectValue unOldVal =_unVal; memcpy(this, &obj, sizeof(SQObjectPtr)); __AddRef(_type,_unVal); __Release(tOldType,unOldVal); return *this; } inline SQObjectPtr& operator=(const SQObject& __restrict obj) { SQObjectType tOldType = _type; SQObjectValue unOldVal =_unVal; memcpy(this, &obj, sizeof(SQObject)); __AddRef(_type,_unVal); __Release(tOldType,unOldVal); return *this; } inline SQObjectPtr& operator=(SQObjectPtr&& __restrict obj) noexcept { if (this != &obj) { __Release(_type, _unVal); memcpy(this, &obj, sizeof(SQObjectPtr)); memset(&obj, 0, sizeof(SQObjectPtr)); // OT_NULL == 0 } return *this; } inline void Null() { SQObjectType tOldType = _type; SQObjectValue unOldVal = _unVal; memset(this,0, sizeof(SQObjectPtr)); _type = OT_NULL; __Release(tOldType ,unOldVal); } private: SQObjectPtr(const char *){} //safety }; inline void _Swap(SQObject &a,SQObject &b) { SQObject t = a; a = b; b = t; } ///////////////////////////////////////////////////////////////////////////////////// #ifndef NO_GARBAGE_COLLECTOR #define MARK_FLAG 0x80000000 struct SQCollectable : public SQRefCounted { SQCollectable *_gc_next; SQCollectable *_gc_prev; SQSharedState *_sharedstate; virtual SQObjectType GetType()=0; virtual void Release()=0; virtual void Mark(SQCollectable **chain)=0; void UnMark(); virtual void Finalize()=0; static void AddToChain(SQCollectable **chain,SQCollectable *c); static void RemoveFromChain(SQCollectable **chain,SQCollectable *c); }; #define ADD_TO_CHAIN(chain,obj) AddToChain(chain,obj) #define REMOVE_FROM_CHAIN(chain,obj) {if(!(_uiRef&MARK_FLAG))RemoveFromChain(chain,obj);} #define CHAINABLE_OBJ SQCollectable #define INIT_CHAIN() {_gc_next=NULL;_gc_prev=NULL;_sharedstate=ss;} #else // Need this to keep SQSharedState pointer to access alloc_ctx // Otherwise, just use SQRefCounted as CHAINABLE_OBJ in the way it was initially struct SQRefCountedWithSharedState : public SQRefCounted { SQSharedState *_sharedstate; }; #define ADD_TO_CHAIN(chain,obj) ((void)0) #define REMOVE_FROM_CHAIN(chain,obj) ((void)0) #define CHAINABLE_OBJ SQRefCountedWithSharedState #define INIT_CHAIN() {_sharedstate=ss;} #endif struct SQDelegable : public CHAINABLE_OBJ { bool SetDelegate(SQTable *m); virtual bool GetMetaMethod(SQVM *v,SQMetaMethod mm,SQObjectPtr &res); SQTable *_delegate; }; inline SQUnsignedInteger TranslateIndex(const SQObjectPtr &idx) { switch(sq_type(idx)){ case OT_NULL: return 0; case OT_INTEGER: return (SQUnsignedInteger)_integer(idx); default: assert(0); break; } return 0; } typedef sqvector SQObjectPtrVec; typedef sqvector SQIntVec; const char *GetTypeName(const SQObject &obj1); const char *IdType2Name(SQObjectType type); #endif //_SQOBJECT_H_ ================================================ FILE: squirrel/sqpcheader.h ================================================ /* see copyright notice in squirrel.h */ #ifndef _SQPCHEADER_H_ #define _SQPCHEADER_H_ #if defined(_MSC_VER) && defined(_DEBUG) #include #endif #include #include #include #include #include #ifdef QUIRREL_HOST_HEADER #define _SQ_QHH_STR(x) #x #define _SQ_QHH_STR2(x) _SQ_QHH_STR(x) #include _SQ_QHH_STR2(QUIRREL_HOST_HEADER) #undef _SQ_QHH_STR #undef _SQ_QHH_STR2 #endif #include //squirrel stuff #include #include "sqobject.h" #include "sqstate.h" #if defined(__cpp_lib_to_chars) && __cpp_lib_to_chars >= 201611L #include #define SQ_USE_STD_FROM_CHARS 1 #endif #ifndef SQ_RUNTIME_TYPE_CHECK #define SQ_RUNTIME_TYPE_CHECK 1 #endif #ifndef SQ_WATCHDOG_ENABLED #define SQ_WATCHDOG_ENABLED 0 #endif #endif //_SQPCHEADER_H_ ================================================ FILE: squirrel/sqstate.cpp ================================================ /* see copyright notice in squirrel.h */ #include "sqpcheader.h" #include "opcodes.h" #include "sqvm.h" #include "sqfuncproto.h" #include "sqclosure.h" #include "sqstring.h" #include "sqtable.h" #include "sqarray.h" #include "squserdata.h" #include "sqclass.h" SQSharedState::SQSharedState(SQAllocContext allocctx) : _alloc_ctx(allocctx), _refs_table(allocctx), defaultLangFeatures(LF_FORBID_DELETE_OP | LF_FORBID_SWITCH_STMT) { _compilererrorhandler = NULL; _compilerdiaghandler = NULL; _printfunc = NULL; _errorfunc = NULL; _lineInfoInExpressions = false; _varTraceEnabled = false; _notifyallexceptions = false; _foreignptr = NULL; _releasehook = NULL; compilationOptions = 0; doc_object_index = 1; rand_seed = 0; watchdog_last_alive_time_msec = 0; watchdog_threshold_msec = 0; } bool CompileTypemask(SQIntVec &res,const char *typemask) { SQInteger i = 0; SQInteger mask = 0; while(typemask[i] != 0) { switch(typemask[i]) { case 'o': mask |= _RT_NULL; break; case 'i': mask |= _RT_INTEGER; break; case 'f': mask |= _RT_FLOAT; break; case 'n': mask |= (_RT_FLOAT | _RT_INTEGER); break; case 's': mask |= _RT_STRING; break; case 't': mask |= _RT_TABLE; break; case 'a': mask |= _RT_ARRAY; break; case 'u': mask |= _RT_USERDATA; break; case 'c': mask |= (_RT_CLOSURE | _RT_NATIVECLOSURE); break; case 'b': mask |= _RT_BOOL; break; case 'g': mask |= _RT_GENERATOR; break; case 'p': mask |= _RT_USERPOINTER; break; case 'v': mask |= _RT_THREAD; break; case 'x': mask |= _RT_INSTANCE; break; case 'y': mask |= _RT_CLASS; break; case 'r': mask |= _RT_WEAKREF; break; case '.': mask = -1; res.push_back(mask); i++; mask = 0; continue; case ' ': i++; continue; //ignores spaces default: return false; } i++; if(typemask[i] == '|') { i++; if(typemask[i] == 0) return false; continue; } res.push_back(mask); mask = 0; } return true; } static SQClass *CreateBuiltInTypeClass(SQSharedState *ss, const char *name, const SQRegFunction *funcz, SQObjectType type_id) { SQClass *cls = (SQClass *)SQ_MALLOC(ss->_alloc_ctx, sizeof(SQClass)); new (cls) SQClass(ss, NULL); cls->_is_builtin_type = true; cls->_builtin_type_id = type_id; if (funcz) { SQInteger i = 0; while (funcz[i].name != 0) { SQNativeClosure *nc = SQNativeClosure::Create(ss, funcz[i].f, 0); nc->_nparamscheck = funcz[i].nparamscheck; nc->_name = SQString::Create(ss, funcz[i].name); if (funcz[i].typemask && !CompileTypemask(nc->_typecheck, funcz[i].typemask)) return NULL; if (funcz[i].pure) nc->_purefunction = true; if (funcz[i].nodiscard) nc->_nodiscard = true; if (funcz[i].docstring) { SQObjectPtr docValue(SQString::Create(ss, funcz[i].docstring)); SQObjectPtr docKey; docKey._type = OT_USERPOINTER; docKey._unVal.pUserPointer = (void *)nc->_function; _table(ss->doc_objects)->NewSlot(docKey, docValue); } cls->NewSlot(ss, SQObjectPtr(nc->_name), SQObjectPtr(nc), /*static*/ true); i++; } } // Lock manually assert(cls->_lockedTypeId == 0); cls->_lockedTypeId = cls->currentHint(); return cls; } void SQSharedState::Init() { _scratchpad=NULL; _scratchpadsize=0; #ifndef NO_GARBAGE_COLLECTOR _gc_chain=NULL; #endif _stringtable = (SQStringTable*)SQ_MALLOC(_alloc_ctx, sizeof(SQStringTable)); new (_stringtable) SQStringTable(this); sq_new(_alloc_ctx, _metamethodnames, SQObjectPtrVec, _alloc_ctx); sq_new(_alloc_ctx, _systemstrings, SQObjectPtrVec, _alloc_ctx); sq_new(_alloc_ctx, _types, SQObjectPtrVec, _alloc_ctx); _metamethodsmap = SQTable::Create(this,MT_NUM_METHODS); #define newsysstring(s) { \ _systemstrings->push_back(SQObjectPtr(SQString::Create(this,s))); \ } //adding type strings to avoid memory trashing //types names newsysstring("null"); newsysstring("table"); newsysstring("array"); newsysstring("closure"); newsysstring("string"); newsysstring("userdata"); newsysstring("integer"); newsysstring("float"); newsysstring("userpointer"); newsysstring("function"); newsysstring("generator"); newsysstring("thread"); newsysstring("class"); newsysstring("instance"); newsysstring("bool"); #undef newsysstring //meta methods #define MM_IMPL(mm, name) { \ _metamethodnames->push_back(SQObjectPtr(SQString::Create(this,name))); \ _table(_metamethodsmap)->NewSlot(_metamethodnames->back(), SQObjectPtr((SQInteger)(mm))); \ } METAMETHODS_LIST #undef MM_IMPL _constructorstr = SQString::Create(this,"constructor"); _registry = SQTable::Create(this,0); _consts = SQTable::Create(this,0); doc_objects = SQTable::Create(this,0); _null_class = CreateBuiltInTypeClass(this, "Null", _null_default_type_methods_funcz, OT_NULL); _integer_class = CreateBuiltInTypeClass(this, "Integer", _integer_default_type_methods_funcz, OT_INTEGER); _float_class = CreateBuiltInTypeClass(this, "Float", _float_default_type_methods_funcz, OT_FLOAT); _bool_class = CreateBuiltInTypeClass(this, "Bool", _bool_default_type_methods_funcz, OT_BOOL); _string_class = CreateBuiltInTypeClass(this, "String", _string_default_type_methods_funcz, OT_STRING); _array_class = CreateBuiltInTypeClass(this, "Array", _array_default_type_methods_funcz, OT_ARRAY); _table_class = CreateBuiltInTypeClass(this, "Table", _table_default_type_methods_funcz, OT_TABLE); _function_class = CreateBuiltInTypeClass(this, "Function", _closure_default_type_methods_funcz, OT_CLOSURE); _generator_class= CreateBuiltInTypeClass(this, "Generator", _generator_default_type_methods_funcz, OT_GENERATOR); _thread_class = CreateBuiltInTypeClass(this, "Thread", _thread_default_type_methods_funcz, OT_THREAD); _class_class = CreateBuiltInTypeClass(this, "Class", _class_default_type_methods_funcz, OT_CLASS); _instance_class = CreateBuiltInTypeClass(this, "Instance", _instance_default_type_methods_funcz, OT_INSTANCE); _weakref_class = CreateBuiltInTypeClass(this, "WeakRef", _weakref_default_type_methods_funcz, OT_WEAKREF); _userdata_class = CreateBuiltInTypeClass(this, "UserData", _userdata_default_type_methods_funcz, OT_USERDATA); } SQSharedState::~SQSharedState() { if(_releasehook) { _releasehook(_thread(_root_vm),_foreignptr,0); _releasehook = NULL; } _constructorstr.Null(); _table(_registry)->Finalize(); _table(_consts)->Finalize(); _table(_metamethodsmap)->Finalize(); _table(doc_objects)->Finalize(); _registry.Null(); _consts.Null(); _metamethodsmap.Null(); doc_objects.Null(); while(!_systemstrings->empty()) { _systemstrings->back().Null(); _systemstrings->pop_back(); } _thread(_root_vm)->Finalize(); _root_vm.Null(); _null_class.Null(); _integer_class.Null(); _float_class.Null(); _bool_class.Null(); _string_class.Null(); _array_class.Null(); _table_class.Null(); _function_class.Null(); _generator_class.Null(); _thread_class.Null(); _class_class.Null(); _instance_class.Null(); _weakref_class.Null(); _userdata_class.Null(); _refs_table.Finalize(); #ifndef NO_GARBAGE_COLLECTOR SQCollectable *t = _gc_chain; SQCollectable *nx = NULL; if(t) { t->_uiRef++; while(t) { t->Finalize(); nx = t->_gc_next; if(nx) nx->_uiRef++; if(--t->_uiRef == 0) t->Release(); t = nx; } } assert(_gc_chain==NULL); //just to proove a theory while(_gc_chain){ _gc_chain->_uiRef++; _gc_chain->Release(); } #endif sq_delete(_alloc_ctx, _types, SQObjectPtrVec); sq_delete(_alloc_ctx, _systemstrings, SQObjectPtrVec); sq_delete(_alloc_ctx, _metamethodnames, SQObjectPtrVec); sq_delete(_alloc_ctx, _stringtable,SQStringTable); if(_scratchpad)SQ_FREE(_alloc_ctx,_scratchpad,_scratchpadsize); } SQInteger SQSharedState::GetMetaMethodIdxByName(const SQObjectPtr &name) { if(sq_type(name) != OT_STRING) return -1; SQObjectPtr ret; if(_table(_metamethodsmap)->Get(name,ret)) { return _integer(ret); } return -1; } #ifndef NO_GARBAGE_COLLECTOR void SQSharedState::MarkObject(SQObjectPtr &o,SQCollectable **chain) { switch(sq_type(o)){ case OT_TABLE:_table(o)->Mark(chain);break; case OT_ARRAY:_array(o)->Mark(chain);break; case OT_USERDATA:_userdata(o)->Mark(chain);break; case OT_CLOSURE:_closure(o)->Mark(chain);break; case OT_NATIVECLOSURE:_nativeclosure(o)->Mark(chain);break; case OT_GENERATOR:_generator(o)->Mark(chain);break; case OT_THREAD:_thread(o)->Mark(chain);break; case OT_CLASS:_class(o)->Mark(chain);break; case OT_INSTANCE:_instance(o)->Mark(chain);break; case OT_OUTER:_outer(o)->Mark(chain);break; case OT_FUNCPROTO:_funcproto(o)->Mark(chain);break; default: break; //shutup compiler } } void SQSharedState::RunMark(SQVM* SQ_UNUSED_ARG(vm),SQCollectable **tchain) { SQVM *vms = _thread(_root_vm); vms->Mark(tchain); _refs_table.Mark(tchain); MarkObject(_registry,tchain); MarkObject(_consts,tchain); MarkObject(_metamethodsmap,tchain); MarkObject(_null_class,tchain); MarkObject(_integer_class,tchain); MarkObject(_float_class,tchain); MarkObject(_bool_class,tchain); MarkObject(_string_class,tchain); MarkObject(_array_class,tchain); MarkObject(_table_class,tchain); MarkObject(_function_class,tchain); MarkObject(_generator_class,tchain); MarkObject(_thread_class,tchain); MarkObject(_class_class,tchain); MarkObject(_instance_class,tchain); MarkObject(_weakref_class,tchain); MarkObject(_userdata_class,tchain); MarkObject(doc_objects,tchain); } SQInteger SQSharedState::ResurrectUnreachable(SQVM *vm) { SQInteger n=0; SQCollectable *tchain=NULL; RunMark(vm,&tchain); SQCollectable *resurrected = _gc_chain; SQCollectable *t = resurrected; _gc_chain = tchain; SQArray *ret = NULL; if(resurrected) { ret = SQArray::Create(this,0); SQCollectable *rlast = NULL; while(t) { rlast = t; SQObjectType type = t->GetType(); if(type != OT_FUNCPROTO && type != OT_OUTER) { SQObject sqo; sqo._type = type; sqo._unVal.pRefCounted = t; sqo._flags = 0; //< FIXME: we lose information on mutability, so it turns everyhing into mutable ret->Append(sqo); } t = t->_gc_next; n++; } assert(rlast->_gc_next == NULL); rlast->_gc_next = _gc_chain; if(_gc_chain) { _gc_chain->_gc_prev = rlast; } _gc_chain = resurrected; } t = _gc_chain; while(t) { t->UnMark(); t = t->_gc_next; } if(ret) { SQObjectPtr temp(ret); vm->Push(temp); } else { vm->PushNull(); } return n; } SQInteger SQSharedState::CollectGarbage(SQVM *vm) { SQInteger n = 0; SQCollectable *tchain = NULL; RunMark(vm,&tchain); SQCollectable *t = _gc_chain; SQCollectable *nx = NULL; if(t) { t->_uiRef++; while(t) { t->Finalize(); nx = t->_gc_next; if(nx) nx->_uiRef++; if(--t->_uiRef == 0) t->Release(); t = nx; n++; } } t = tchain; while(t) { t->UnMark(); t = t->_gc_next; } _gc_chain = tchain; return n; } #endif #ifndef NO_GARBAGE_COLLECTOR void SQCollectable::AddToChain(SQCollectable **chain,SQCollectable *c) { c->_gc_prev = NULL; c->_gc_next = *chain; if(*chain) (*chain)->_gc_prev = c; *chain = c; } void SQCollectable::RemoveFromChain(SQCollectable **chain,SQCollectable *c) { if(c->_gc_prev) c->_gc_prev->_gc_next = c->_gc_next; else *chain = c->_gc_next; if(c->_gc_next) c->_gc_next->_gc_prev = c->_gc_prev; c->_gc_next = NULL; c->_gc_prev = NULL; } #endif char* SQSharedState::GetScratchPad(SQInteger size) { SQInteger newsize; if(size>0) { if(_scratchpadsize < size) { newsize = size + (size>>1); _scratchpad = (char *)SQ_REALLOC(_alloc_ctx, _scratchpad, _scratchpadsize, newsize); _scratchpadsize = newsize; }else if(_scratchpadsize >= (size<<5)) { newsize = _scratchpadsize >> 1; _scratchpad = (char *)SQ_REALLOC(_alloc_ctx, _scratchpad, _scratchpadsize, newsize); _scratchpadsize = newsize; } } return _scratchpad; } RefTable::RefTable(SQAllocContext ctx) : _alloc_ctx(ctx) { AllocNodes(4); } void RefTable::Finalize() { RefNode *nodes = _nodes; for(SQUnsignedInteger n = 0; n < _numofslots; n++) { nodes->obj.Null(); nodes++; } } RefTable::~RefTable() { SQ_FREE(_alloc_ctx, _buckets,(_numofslots * sizeof(RefNode *)) + (_numofslots * sizeof(RefNode))); } #ifndef NO_GARBAGE_COLLECTOR void RefTable::Mark(SQCollectable **chain) { RefNode *nodes = (RefNode *)_nodes; for(SQUnsignedInteger n = 0; n < _numofslots; n++) { if(sq_type(nodes->obj) != OT_NULL) { SQSharedState::MarkObject(nodes->obj,chain); } nodes++; } } #endif void RefTable::AddRef(SQObject &obj) { SQHash mainpos; RefNode *prev; RefNode *ref = Get(obj,mainpos,&prev,true); ref->refs++; } SQUnsignedInteger RefTable::GetRefCount(SQObject &obj) { SQHash mainpos; RefNode *prev; RefNode *ref = Get(obj,mainpos,&prev,false); return ref ? ref->refs : 0; } SQBool RefTable::Release(SQObject &obj) { SQHash mainpos; RefNode *prev; RefNode *ref = Get(obj,mainpos,&prev,false); if(ref) { if(--ref->refs == 0) { SQObjectPtr o = ref->obj; if(prev) { prev->next = ref->next; } else { _buckets[mainpos] = ref->next; } ref->next = _freelist; _freelist = ref; _slotused--; ref->obj.Null(); //<>test for shrink? return SQTrue; } } else { assert(0); } return SQFalse; } void RefTable::Resize(SQUnsignedInteger size) { RefNode **oldbucks = _buckets; RefNode *t = _nodes; SQUnsignedInteger oldnumofslots = _numofslots; AllocNodes(size); //rehash SQUnsignedInteger nfound = 0; for(SQUnsignedInteger n = 0; n < oldnumofslots; n++) { if(sq_type(t->obj) != OT_NULL) { //add back; assert(t->refs != 0); RefNode *nn = Add(::HashObj(t->obj)&(_numofslots-1),t->obj); nn->refs = t->refs; t->obj.Null(); nfound++; } t++; } (void)nfound; assert(nfound == oldnumofslots); SQ_FREE(_alloc_ctx, oldbucks,(oldnumofslots * sizeof(RefNode *)) + (oldnumofslots * sizeof(RefNode))); } RefTable::RefNode *RefTable::Add(SQHash mainpos,SQObject &obj) { RefNode *t = _buckets[mainpos]; RefNode *newnode = _freelist; newnode->obj = obj; _buckets[mainpos] = newnode; _freelist = _freelist->next; newnode->next = t; assert(newnode->refs == 0); _slotused++; return newnode; } RefTable::RefNode *RefTable::Get(SQObject &obj,SQHash &mainpos,RefNode **prev,bool add) { RefNode *ref; mainpos = ::HashObj(obj)&(_numofslots-1); *prev = NULL; for (ref = _buckets[mainpos]; ref; ) { if(_rawval(ref->obj) == _rawval(obj) && sq_type(ref->obj) == sq_type(obj)) break; *prev = ref; ref = ref->next; } if(ref == NULL && add) { if(_numofslots == _slotused) { assert(_freelist == 0); Resize(_numofslots*2); mainpos = ::HashObj(obj)&(_numofslots-1); } ref = Add(mainpos,obj); } return ref; } void RefTable::AllocNodes(SQUnsignedInteger size) { RefNode **bucks; RefNode *nodes; bucks = (RefNode **)SQ_MALLOC(_alloc_ctx, (size * sizeof(RefNode *)) + (size * sizeof(RefNode))); nodes = (RefNode *)&bucks[size]; RefNode *temp = nodes; SQUnsignedInteger n; for(n = 0; n < size - 1; n++) { bucks[n] = NULL; temp->refs = 0; new (&temp->obj) SQObjectPtr; temp->next = temp+1; temp++; } bucks[n] = NULL; temp->refs = 0; new (&temp->obj) SQObjectPtr; temp->next = NULL; _freelist = nodes; _nodes = nodes; _buckets = bucks; _slotused = 0; _numofslots = size; } ////////////////////////////////////////////////////////////////////////// //SQStringTable /* * The following code is based on Lua 4.0 (Copyright 1994-2002 Tecgraf, PUC-Rio.) * http://www.lua.org/copyright.html#4 * http://www.lua.org/source/4.0.1/src_lstring.c.html */ SQStringTable::SQStringTable(SQSharedState *ss) { _sharedstate = ss; AllocNodes(4); _slotused = 0; } SQStringTable::~SQStringTable() { SQ_FREE(_sharedstate->_alloc_ctx, _strings,sizeof(SQString*)*_numofslots); _strings = NULL; } void SQStringTable::AllocNodes(SQInteger size) { _numofslots = size; _strings = (SQString**)SQ_MALLOC(_sharedstate->_alloc_ctx, sizeof(SQString*)*_numofslots); memset(_strings,0,sizeof(SQString*)*_numofslots); } SQString *SQStringTable::Add(const char *news,SQInteger len) { if(len<0) len = (SQInteger)strlen(news); SQHash newhash = ::_hashstr(news,len); SQHash h = newhash&(_numofslots-1); SQString *s; for (s = _strings[h]; s; s = s->_next){ if(s->_len == len && (!memcmp(news,s->_val,len))) return s; //found } SQString *t = (SQString *)SQ_MALLOC(_sharedstate->_alloc_ctx, len+sizeof(SQString)); new (t) SQString; t->_sharedstate = _sharedstate; memcpy(t->_val,news,len); t->_val[len] = '\0'; t->_len = len; t->_hash = newhash; t->_next = _strings[h]; _strings[h] = t; _slotused++; if (_slotused > _numofslots) /* too crowded? */ Resize(_numofslots*2); return t; } void SQStringTable::Resize(SQInteger size) { SQInteger oldsize=_numofslots; SQString **oldtable=_strings; AllocNodes(size); for (SQInteger i=0; i_next; SQHash h = p->_hash&(_numofslots-1); p->_next = _strings[h]; _strings[h] = p; p = next; } } SQ_FREE(_sharedstate->_alloc_ctx, oldtable, oldsize*sizeof(SQString*)); } void SQStringTable::Remove(SQString *bs) { SQString *s; SQString *prev=NULL; SQHash h = bs->_hash&(_numofslots - 1); for (s = _strings[h]; s; ){ if(s == bs){ if(prev) prev->_next = s->_next; else _strings[h] = s->_next; _slotused--; SQInteger slen = s->_len; s->~SQString(); SQ_FREE(_sharedstate->_alloc_ctx, s, sizeof(SQString) + slen); return; } prev = s; s = s->_next; } assert(0);//if this fail something is wrong } ================================================ FILE: squirrel/sqstate.h ================================================ /* see copyright notice in squirrel.h */ #ifndef _SQSTATE_H_ #define _SQSTATE_H_ #include "squtils.h" #include "sqobject.h" struct SQString; struct SQTable; struct SQStringTable { SQStringTable(SQSharedState*ss); ~SQStringTable(); SQString *Add(const char *,SQInteger len); void Remove(SQString *); private: void Resize(SQInteger size); void AllocNodes(SQInteger size); SQString **_strings; SQUnsignedInteger _numofslots; SQUnsignedInteger _slotused; SQSharedState *_sharedstate; }; struct RefTable { struct RefNode { SQObjectPtr obj; SQUnsignedInteger refs; struct RefNode *next; }; RefTable(SQAllocContext ctx); ~RefTable(); void AddRef(SQObject &obj); SQBool Release(SQObject &obj); SQUnsignedInteger GetRefCount(SQObject &obj); #ifndef NO_GARBAGE_COLLECTOR void Mark(SQCollectable **chain); #endif void Finalize(); private: RefNode *Get(SQObject &obj,SQHash &mainpos,RefNode **prev,bool add); RefNode *Add(SQHash mainpos,SQObject &obj); void Resize(SQUnsignedInteger size); void AllocNodes(SQUnsignedInteger size); SQUnsignedInteger _numofslots; SQUnsignedInteger _slotused; RefNode *_nodes; RefNode *_freelist; RefNode **_buckets; SQAllocContext _alloc_ctx; }; #define ADD_STRING(ss,str,len) ss->_stringtable->Add(str,len) #define REMOVE_STRING(ss,bstr) ss->_stringtable->Remove(bstr) struct SQObjectPtr; struct SQSharedState { SQSharedState(SQAllocContext allocctx); ~SQSharedState(); void Init(); public: bool checkCompilationOption(SQUnsignedInteger co) const { return (compilationOptions & co) != 0; } void enableCompilationOption(SQUnsignedInteger co) { compilationOptions |= co; } void disableCompilationOption(SQUnsignedInteger co) { compilationOptions &= ~co; } char* GetScratchPad(SQInteger size); SQInteger GetMetaMethodIdxByName(const SQObjectPtr &name); #ifndef NO_GARBAGE_COLLECTOR SQInteger CollectGarbage(SQVM *vm); void RunMark(SQVM *vm,SQCollectable **tchain); SQInteger ResurrectUnreachable(SQVM *vm); static void MarkObject(SQObjectPtr &o,SQCollectable **chain); #endif SQAllocContext _alloc_ctx; SQObjectPtrVec *_metamethodnames; SQObjectPtr _metamethodsmap; SQObjectPtrVec *_systemstrings; SQObjectPtrVec *_types; SQStringTable *_stringtable; RefTable _refs_table; SQObjectPtr _registry; SQObjectPtr _consts; SQObjectPtr _constructorstr; #ifndef NO_GARBAGE_COLLECTOR SQCollectable *_gc_chain; #endif SQObjectPtr _root_vm; // Built-in type classes SQObjectPtr _null_class; SQObjectPtr _integer_class; SQObjectPtr _float_class; SQObjectPtr _bool_class; SQObjectPtr _string_class; SQObjectPtr _array_class; SQObjectPtr _table_class; SQObjectPtr _function_class; SQObjectPtr _generator_class; SQObjectPtr _thread_class; SQObjectPtr _class_class; SQObjectPtr _instance_class; SQObjectPtr _weakref_class; SQObjectPtr _userdata_class; static const SQRegFunction _table_default_type_methods_funcz[]; static const SQRegFunction _array_default_type_methods_funcz[]; static const SQRegFunction _string_default_type_methods_funcz[]; static const SQRegFunction _integer_default_type_methods_funcz[]; static const SQRegFunction _float_default_type_methods_funcz[]; static const SQRegFunction _bool_default_type_methods_funcz[]; static const SQRegFunction _null_default_type_methods_funcz[]; static const SQRegFunction _generator_default_type_methods_funcz[]; static const SQRegFunction _closure_default_type_methods_funcz[]; static const SQRegFunction _thread_default_type_methods_funcz[]; static const SQRegFunction _class_default_type_methods_funcz[]; static const SQRegFunction _instance_default_type_methods_funcz[]; static const SQRegFunction _weakref_default_type_methods_funcz[]; static const SQRegFunction _userdata_default_type_methods_funcz[]; SQCOMPILERERROR _compilererrorhandler; SQPRINTFUNCTION _printfunc; SQPRINTFUNCTION _errorfunc; SQ_COMPILER_DIAG_CB _compilerdiaghandler; bool _notifyallexceptions; bool _lineInfoInExpressions; bool _varTraceEnabled; SQUnsignedInteger compilationOptions; SQUnsignedInteger defaultLangFeatures; SQUserPointer _foreignptr; SQRELEASEHOOK _releasehook; SQObjectPtr doc_objects; int doc_object_index; SQUnsignedInteger32 rand_seed; SQUnsignedInteger32 watchdog_last_alive_time_msec; SQUnsignedInteger32 watchdog_threshold_msec; private: char *_scratchpad; SQInteger _scratchpadsize; }; #define _sp(s) (_sharedstate->GetScratchPad(s)) #define _spval (_sharedstate->GetScratchPad(-1)) bool CompileTypemask(SQIntVec &res,const char *typemask); #endif //_SQSTATE_H_ ================================================ FILE: squirrel/sqstring.h ================================================ /* see copyright notice in squirrel.h */ #ifndef _SQSTRING_H_ #define _SQSTRING_H_ inline SQHash _hashstr_lua5(const char *s, size_t l) { SQHash h = (SQHash)l; /* seed */ size_t step = (l>>5)+1; /* if string is too long, don't hash all its chars */ for (; l>=step; l-=step) h ^= ((h<<5)+(h>>2)+(char)(s[l-1])); return h; } //djb2 inline SQHash _hashstr_djb2(const char *s, size_t l) { SQHash hash = SQHash(5381+l); size_t step = (l>>5)+1; /* if string is too long, don't hash all its chars */ for (; l>=step; l-=step) hash = hash * 33 + s[l-1]; return hash; } //fnv1 inline uint32_t _hashstr_fnv1a(const char *s, size_t l) { uint32_t result = 2166136261U; size_t step = (l>>5)+1; /* if string is too long, don't hash all its chars */ for (; l>=step; l-=step) result = (result ^ s[l-1])* 16777619; return result; } inline uint64_t _hashstr_fnv1a_64(const char *s, size_t l) { uint64_t result = 14695981039346656037LU; size_t step = (l>>5)+1; /* if string is too long, don't hash all its chars */ for (; l>=step; l-=step) result = (result ^ s[l-1]) * 1099511628211LU; return result; } //inline SQHash _hashstr (const char *s, size_t l){return _hashstr_lua5(s, l);}//worst //inline SQHash _hashstr (const char *s, size_t l){return _hashstr_djb2(s, l);}//good #ifdef _SQ64//assume we run on 64 bit platform inline SQHash _hashstr (const char *s, size_t l){return _hashstr_fnv1a_64(s, l);} #else inline SQHash _hashstr (const char *s, size_t l){return _hashstr_fnv1a(s, l);} #endif struct SQString : public SQRefCounted { SQString(){} ~SQString(){} public: static SQString *Create(SQSharedState *ss, const char *, SQInteger len = -1 ); SQInteger Next(const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval); void Release(); SQSharedState *_sharedstate; SQString *_next; //chain for the string table SQInteger _len; SQHash _hash; char _val[1]; }; #endif //_SQSTRING_H_ ================================================ FILE: squirrel/sqstringlib.cpp ================================================ #include #include #include #include #include static void __strip_l(const char *str,const char **start) { const char *t = str; while(((*t) != '\0') && sq_isspace(*t)){ t++; } *start = t; } static void __strip_r(const char *str,SQInteger len,const char **end) { if(len == 0) { *end = str; return; } const char *t = &str[len-1]; while(t >= str && sq_isspace(*t)) { t--; } *end = t + 1; } SQInteger _sq_string_strip_impl(HSQUIRRELVM v, SQInteger arg_stack_start) { const char *str,*start,*end; sq_getstring(v,arg_stack_start,&str); SQInteger len = sq_getsize(v,arg_stack_start); __strip_l(str,&start); __strip_r(str,len,&end); sq_pushstring(v,start,end - start); return 1; } SQInteger _sq_string_lstrip_impl(HSQUIRRELVM v, SQInteger arg_stack_start) { const char *str,*start; sq_getstring(v,arg_stack_start,&str); __strip_l(str,&start); sq_pushstring(v,start,-1); return 1; } SQInteger _sq_string_rstrip_impl(HSQUIRRELVM v, SQInteger arg_stack_start) { const char *str,*end; sq_getstring(v,arg_stack_start,&str); SQInteger len = sq_getsize(v,arg_stack_start); __strip_r(str,len,&end); sq_pushstring(v,str,end - str); return 1; } SQInteger _sq_string_split_by_chars_impl(HSQUIRRELVM v, SQInteger arg_stack_start) { const char *str,*seps; SQInteger sepsize; SQBool skipempty = SQFalse; sq_getstring(v,arg_stack_start,&str); sq_getstringandsize(v,arg_stack_start+1,&seps,&sepsize); if(sepsize == 0) return sq_throwerror(v,"empty separators string"); if(sq_gettop(v)>arg_stack_start+1) { sq_getbool(v,arg_stack_start+2,&skipempty); } const char *start = str; const char *end = str; sq_newarray(v,0); while(*end != '\0') { char cur = *end; for(SQInteger i = 0; i < sepsize; i++) { if(cur == seps[i]) { if(!skipempty || (end != start)) { sq_pushstring(v,start,end-start); sq_arrayappend(v,-2); } start = end + 1; break; } } end++; } if(end != start) { sq_pushstring(v,start,end-start); sq_arrayappend(v,-2); } return 1; } SQInteger _sq_string_escape_impl(HSQUIRRELVM v, SQInteger arg_stack_start) { const char *str; char *dest,*resstr; SQInteger size; sq_getstringandsize(v,arg_stack_start,&str,&size); if(size == 0) { sq_push(v,arg_stack_start); return 1; } const char *escpat = "\\x%02x"; const SQInteger maxescsize = 4; SQInteger destcharsize = (size * maxescsize) + 1; // +1 for null terminator of last escape resstr = dest = (char *)sq_getscratchpad(v,destcharsize * sizeof(char)); char c; char escch; SQInteger escaped = 0; for(int n = 0; n < size; n++){ c = *str++; escch = 0; if(sq_isprint(c) || c == 0) { switch(c) { case '\a': escch = 'a'; break; case '\b': escch = 'b'; break; case '\t': escch = 't'; break; case '\n': escch = 'n'; break; case '\v': escch = 'v'; break; case '\f': escch = 'f'; break; case '\r': escch = 'r'; break; case '\\': escch = '\\'; break; case '\"': escch = '\"'; break; case '\'': escch = '\''; break; case 0: escch = '0'; break; } if(escch) { *dest++ = '\\'; *dest++ = escch; escaped++; } else { *dest++ = c; } } else { dest += scsprintf(dest, maxescsize + 1, escpat, (unsigned char)c); escaped++; } } if(escaped) { sq_pushstring(v,resstr,dest - resstr); } else { sq_push(v,arg_stack_start); //nothing escaped } return 1; } SQInteger _sq_string_startswith_impl(HSQUIRRELVM v, SQInteger arg_stack_start) { const char *str,*cmp; SQInteger len, cmplen; sq_getstringandsize(v,arg_stack_start,&str,&len); sq_getstringandsize(v,arg_stack_start+1,&cmp,&cmplen); SQBool ret = SQFalse; if(cmplen <= len) { ret = memcmp(str,cmp,cmplen) == 0 ? SQTrue : SQFalse; } sq_pushbool(v,ret); return 1; } SQInteger _sq_string_endswith_impl(HSQUIRRELVM v, SQInteger arg_stack_start) { const char *str,*cmp; SQInteger len, cmplen; sq_getstringandsize(v,arg_stack_start,&str,&len); sq_getstringandsize(v,arg_stack_start+1,&cmp,&cmplen); SQBool ret = SQFalse; if(cmplen <= len) { ret = memcmp(&str[len - cmplen],cmp,cmplen) == 0 ? SQTrue : SQFalse; } sq_pushbool(v,ret); return 1; } ================================================ FILE: squirrel/sqtable.cpp ================================================ /* see copyright notice in squirrel.h */ #include "sqpcheader.h" #include "sqvm.h" #include "sqtable.h" #include "sqfuncproto.h" #include "sqclosure.h" #include #if defined(_MSC_VER) && !defined(__clang__) #pragma intrinsic(_BitScanReverse) #endif #define _FAST_CLONE #define MINPOWER2 1 #define CLASS_TYPE_HASH_INIT 14695981039346656037ul #define CLASS_TYPE_HASH_PRIME 1099511628211ul static inline uint64_t class_type_hash_update_1(uint64_t hashval, uint8_t x) { return (hashval ^ x) * CLASS_TYPE_HASH_PRIME; } static inline uint64_t class_type_hash_update_4(uint64_t hashval, uint32_t x) { // Unrolled FNV1 const uint8_t *bytes = (const uint8_t *)&x; hashval = (hashval ^ bytes[0]) * CLASS_TYPE_HASH_PRIME; hashval = (hashval ^ bytes[1]) * CLASS_TYPE_HASH_PRIME; hashval = (hashval ^ bytes[2]) * CLASS_TYPE_HASH_PRIME; hashval = (hashval ^ bytes[3]) * CLASS_TYPE_HASH_PRIME; return hashval; } SQTable::SQTable(SQSharedState *ss,SQInteger nInitialSize) : _alloc_ctx(ss->_alloc_ctx) , _classTypeId(0) { SQInteger pow2size=MINPOWER2; while(nInitialSize>pow2size)pow2size=pow2size<<1; AllocNodes(pow2size); _usednodes = 0; _delegate = NULL; INIT_CHAIN(); ADD_TO_CHAIN(&_sharedstate->_gc_chain,this); } void SQTable::Remove(const SQObjectPtr &key) { _HashNode *n = _Get(key, HashObj(key) & _numofnodes_minus_one); if (n) { n->val.Null(); n->key.Null(); n->key._type = OT_FREE_TABLE_SLOT; VT_CLEAR_SINGLE(n); _usednodes--; Rehash(false); _classTypeId = 0; } } void SQTable::AllocNodes(SQInteger nSize) { assert((nSize & (nSize-1)) == 0); // pow2 _HashNode *nodes=(_HashNode *)SQ_MALLOC(_alloc_ctx, sizeof(_HashNode)*nSize); _numofnodes_minus_one=(uint32_t)(nSize-1); _nodes=nodes; _firstfree=&_nodes[_numofnodes_minus_one]; for (_HashNode *i = nodes, *e = i + nSize; i != e; i++) new (i) _HashNode; } void SQTable::Rehash(bool force) { SQInteger oldsize=_numofnodes_minus_one+1; //prevent problems with the integer division if (oldsize < MINPOWER2) oldsize = MINPOWER2; _HashNode *nold=_nodes; SQInteger nelems=CountUsed(); if (nelems >= oldsize - oldsize/4) /* using more than 3/4? */ AllocNodes(oldsize*2); else if (nelems < oldsize/4 && /* less than 1/4? */ oldsize > MINPOWER2) AllocNodes(oldsize/2); else if (force) { assert(oldsize > 0); #if !defined(_MSC_VER) || defined(__clang__) unsigned log2 = 31 - __builtin_clz((unsigned)oldsize); #else unsigned long log2; _BitScanReverse(&log2, oldsize); #endif AllocNodes(unsigned(1 << (log2 + 1))); assert(_numofnodes_minus_one + 1 > oldsize); } else return; _usednodes = 0; for (SQInteger i=0; ikey) & OT_FREE_TABLE_SLOT)) NewSlot(old->key,old->val VT_REF(old)); } for(SQInteger k=0;k_nodes; _HashNode *__restrict src = _nodes; _HashNode *__restrict dst = nt->_nodes; for(_HashNode *__restrict srcE=src + cnt; src != srcE; src++, dst++) { dst->key = src->key; dst->val = src->val; VT_COPY_SINGLE(src, dst); VT_TRACE_SINGLE(dst, dst->val, _ss(this)->_root_vm); if(src->next) { assert(src->next > basesrc); dst->next = basedst + (src->next - basesrc); assert(dst != dst->next); } } assert(_firstfree >= basesrc); assert(_firstfree != NULL); nt->_firstfree = basedst + (_firstfree - basesrc); nt->_usednodes = _usednodes; #else SQInteger ridx=0; SQObjectPtr key,val; while((ridx=Next(true,ridx,key,val))!=-1){ nt->NewSlot(key,val VT_CODE(VT_COMMA &_nodes[ridx].varTrace)); } #endif nt->_classTypeId = _classTypeId; nt->SetDelegate(_delegate); return nt; } SQTable::_HashNode *SQTable::_Get(const SQObjectPtr &key) const { if (sq_type(key) == OT_STRING) return _GetStr(_rawval(key), _string(key)->_hash & _numofnodes_minus_one); else return _Get(key, HashObj(key) & _numofnodes_minus_one); } bool SQTable::Get(const SQObjectPtr &key,SQObjectPtr &val) const { const _HashNode *n = _Get(key); if (n) { val = _realval(n->val); return true; } return false; } bool SQTable::GetStrToInt(const SQObjectPtr &key,uint32_t &val) const//for class members { assert(sq_type(key) == OT_STRING); const _HashNode *n = _GetStr(_rawval(key), _string(key)->_hash & _numofnodes_minus_one); if (!n) return false; assert(sq_type(n->val) == OT_INTEGER); val = _integer(n->val); return true; } #if SQ_VAR_TRACE_ENABLED == 1 VarTrace * SQTable::GetVarTracePtr(const SQObjectPtr &key) { _HashNode *n = _Get(key, HashObj(key) & _numofnodes_minus_one); if (n) return &(n->varTrace); else return NULL; } #endif bool SQTable::NewSlot(const SQObjectPtr &__restrict key,const SQObjectPtr &__restrict val VT_DECL_ARG) { SQHash h = HashObj(key) & _numofnodes_minus_one; _HashNode *n = _Get(key, h); if (n) { n->val = val; VT_CODE(if (var_trace_arg) n->varTrace = *var_trace_arg); VT_TRACE_SINGLE(n, val, _ss(this)->_root_vm); return false; } _HashNode *mp = &_nodes[h]; n = mp; //key not found I'll insert it //main pos is not free if(!(sq_type(mp->key) & OT_FREE_TABLE_SLOT)) { n = _firstfree; /* get a free place */ SQHash mph = HashObj(mp->key) & _numofnodes_minus_one; _HashNode *othern; /* main position of colliding node */ if (mp > n && (othern = &_nodes[mph]) != mp){ /* yes; move colliding node into free position */ while (othern->next != mp){ assert(othern->next != NULL); othern = othern->next; /* find previous */ } othern->next = n; /* redo the chain with `n' in place of `mp' */ n->key = mp->key; n->val = mp->val;/* copy colliding node into free pos. (mp->next also goes) */ n->next = mp->next; VT_COPY_SINGLE(mp, n); mp->key.Null(); mp->key._type = OT_FREE_TABLE_SLOT; mp->val.Null(); VT_CLEAR_SINGLE(mp); mp->next = NULL; /* now `mp' is free */ } else{ /* new node will go into free position */ n->next = mp->next; /* chain new position */ mp->next = n; mp = n; } } mp->key = key; for (;;) { /* correct `firstfree' */ if ((sq_type(_firstfree->key) & OT_FREE_TABLE_SLOT) && _firstfree->next == NULL) { mp->val = val; VT_CODE(if (var_trace_arg) mp->varTrace = *var_trace_arg); VT_TRACE_SINGLE(mp, val, _ss(this)->_root_vm); // update class type id if (sq_isstring(key) && (_numofnodes_minus_one < (1<> 3) & 0xFFFFFFFF ); _classTypeId = class_type_hash_update_4(_classTypeId, strIdBits); } } else { _classTypeId = 0; } ++_usednodes; return true; /* OK; table still has a free place */ } else if (_firstfree == _nodes) break; /* cannot decrement from here */ else (_firstfree)--; } Rehash(true); return NewSlot(key, val VT_CODE(VT_COMMA var_trace_arg)); } SQInteger SQTable::Next(bool getweakrefs,const SQObjectPtr &__restrict refpos, SQObjectPtr &__restrict outkey, SQObjectPtr &__restrict outval) { uint32_t idx = (uint32_t)TranslateIndex(refpos); while (idx <= _numofnodes_minus_one) { if(!(sq_type(_nodes[idx].key) & OT_FREE_TABLE_SLOT)) { //first found _HashNode &n = _nodes[idx]; outkey = n.key; outval = getweakrefs?(SQObject)n.val:_realval(n.val); //return idx for the next iteration return ++idx; } ++idx; } //nothing to iterate anymore return -1; } bool SQTable::Set(const SQObjectPtr &key, const SQObjectPtr &val) { _HashNode *n = _Get(key); if (n) { n->val = val; VT_TRACE_SINGLE(n, val, _ss(this)->_root_vm); return true; } return false; } void SQTable::_ClearNodes() { for (_HashNode *__restrict i = _nodes, *e = i + _numofnodes_minus_one; i <= e; i++) { i->key.Null(); i->key._type = OT_FREE_TABLE_SLOT; i->val.Null(); i->next = NULL; VT_CLEAR_SINGLE(i); } } void SQTable::Finalize() { _ClearNodes(); SetDelegate(NULL); } void SQTable::Clear(SQBool rehash) { _ClearNodes(); _usednodes = 0; _classTypeId = 0; if (rehash) Rehash(true); else _firstfree=&_nodes[_numofnodes_minus_one]; } bool SQTable::IsBinaryEqual(SQTable *o) { if (o->_usednodes != _usednodes || o->_classTypeId != _classTypeId) return false; if (o == this) return true; if (o->_usednodes == 0) return true; return memcmp(_nodes, o->_nodes, sizeof(_HashNode) * (_numofnodes_minus_one + 1)) == 0; } ================================================ FILE: squirrel/sqtable.h ================================================ /* see copyright notice in squirrel.h */ #ifndef _SQTABLE_H_ #define _SQTABLE_H_ /* * The following code is based on Lua 4.0 (Copyright 1994-2002 Tecgraf, PUC-Rio.) * http://www.lua.org/copyright.html#4 * http://www.lua.org/source/4.0.1/src_ltable.c.html */ #include "sqstring.h" #include "vartrace.h" #define hashptr(p) (SQHash((SQInteger(p) >> 4))) #define TBL_CLASS_TYPE_MEMBER_BITS 5 #define TBL_CLASS_TYPE_MEMBER_MASK ((1u<> 23); } inline SQHash HashObj(const SQObject &key) { switch(sq_type(key)) { case OT_STRING: return _string(key)->_hash; case OT_FLOAT: return (SQHash)(sq_float_hash32(_float(key))); case OT_BOOL: case OT_INTEGER: return (SQHash)((SQInteger)_integer(key)); default: return hashptr(key._unVal.pRefCounted); } } struct SQTable : public SQDelegable { private: friend struct SQVM; friend struct SQDeduplicateShrinker; friend struct SQStreamSerializer; struct _HashNode { _HashNode() { key._type = OT_FREE_TABLE_SLOT; next = NULL; } SQObjectPtr val; SQObjectPtr key; _HashNode *next; VT_DECL_SINGLE; }; _HashNode *_firstfree; _HashNode *_nodes; uint32_t _numofnodes_minus_one; uint32_t _usednodes; SQAllocContext _alloc_ctx; uint64_t _classTypeId; /////////////////////// void AllocNodes(SQInteger nSize); void Rehash(bool force); SQTable(SQSharedState *ss, SQInteger nInitialSize); void _ClearNodes(); public: static SQTable* Create(SQSharedState *ss,SQInteger nInitialSize) { SQTable *newtable = (SQTable*)SQ_MALLOC(ss->_alloc_ctx, sizeof(SQTable)); new (newtable) SQTable(ss, nInitialSize); newtable->_delegate = NULL; return newtable; } void Finalize(); SQTable *Clone(); ~SQTable() { uint32_t cnt = _numofnodes_minus_one + 1; _HashNode *__restrict lNodes = _nodes; SetDelegate(NULL); REMOVE_FROM_CHAIN(&_sharedstate->_gc_chain, this); for (_HashNode *i = lNodes, *e = i + cnt; i != e; i++) i->~_HashNode(); SQ_FREE(_alloc_ctx, lNodes, cnt * sizeof(_HashNode)); } #ifndef NO_GARBAGE_COLLECTOR void Mark(SQCollectable **chain); SQObjectType GetType() {return OT_TABLE;} #endif inline _HashNode *_GetStr(const SQRawObjectVal key, SQHash hash) const { _HashNode *n = &_nodes[hash]; do{ if(_rawval(n->key) == key && sq_type(n->key) == OT_STRING){ return n; } }while((n = n->next)); return NULL; } inline _HashNode *_Get(const SQObjectPtr &key, SQHash hash) const { _HashNode *n = &_nodes[hash]; do{ if(_rawval(n->key) == _rawval(key) && sq_type(n->key) == sq_type(key)){ return n; } }while((n = n->next)); return NULL; } //for compiler use inline bool GetStr(const char* key,SQInteger keylen,SQObjectPtr &val) const { SQHash hash = _hashstr(key,keylen); _HashNode *n = &_nodes[hash & _numofnodes_minus_one]; _HashNode *res = NULL; do{ if (sq_type(n->key) == OT_STRING && (keylen == _string(n->key)->_len && strncmp(_stringval(n->key), key, keylen) == 0)) { res = n; break; } }while((n = n->next)); if (res) { val = _realval(res->val); return true; } return false; } _HashNode *_Get(const SQObjectPtr &key) const; bool Get(const SQObjectPtr &key,SQObjectPtr &val) const; bool GetStrToInt(const SQObjectPtr &key,uint32_t &val) const;//for class members inline _HashNode *GetNodeFromTypeHint(uint64_t hint, const SQObjectPtr &key) const { size_t nodeIdx = size_t(hint & TBL_CLASS_TYPE_MEMBER_MASK); if (nodeIdx > _numofnodes_minus_one) assert(!"Node index is out of range"); else if (!sq_isstring(_nodes[nodeIdx].key)) assert(!"Node key is not a string"); else if (_string(_nodes[nodeIdx].key) != _string(key)) assert(!"Literal key mismatch"); else return _nodes + nodeIdx; return nullptr; } VT_CODE(VarTrace * GetVarTracePtr(const SQObjectPtr &key)); void Remove(const SQObjectPtr &key); bool Set(const SQObjectPtr &key, const SQObjectPtr &val); //returns true if a new slot has been created false if it was already present bool NewSlot(const SQObjectPtr &key,const SQObjectPtr &val VT_DECL_ARG_DEF); SQInteger Next(bool getweakrefs,const SQObjectPtr &refpos, SQObjectPtr &outkey, SQObjectPtr &outval); SQInteger CountUsed(){ return _usednodes;} SQInteger AllocatedNodes(){ return _numofnodes_minus_one + 1; } bool IsBinaryEqual(SQTable *o); void Clear(SQBool rehash = SQTrue); void Release() { sq_delete(_alloc_ctx, this, SQTable); } }; #endif //_SQTABLE_H_ ================================================ FILE: squirrel/squserdata.h ================================================ /* see copyright notice in squirrel.h */ #ifndef _SQUSERDATA_H_ #define _SQUSERDATA_H_ struct SQUserData : SQDelegable { SQUserData(SQSharedState *ss){ _delegate = 0; _hook = NULL; INIT_CHAIN(); ADD_TO_CHAIN(&_ss(this)->_gc_chain, this); } ~SQUserData() { REMOVE_FROM_CHAIN(&_ss(this)->_gc_chain, this); SetDelegate(NULL); } static SQUserData* Create(SQSharedState *ss, SQInteger size) { SQUserData* ud = (SQUserData*)SQ_MALLOC(ss->_alloc_ctx, sq_aligning(sizeof(SQUserData))+size); new (ud) SQUserData(ss); ud->_size = size; ud->_typetag = 0; return ud; } #ifndef NO_GARBAGE_COLLECTOR void Mark(SQCollectable **chain); void Finalize(){SetDelegate(NULL);} SQObjectType GetType(){ return OT_USERDATA;} #endif void Release() { if (_hook) _hook(_thread(_sharedstate->_root_vm),(SQUserPointer)sq_aligning(this + 1),_size); SQInteger tsize = _size; this->~SQUserData(); SQ_FREE(_sharedstate->_alloc_ctx, this, sq_aligning(sizeof(SQUserData)) + tsize); } SQInteger _size; SQRELEASEHOOK _hook; SQUserPointer _typetag; //char _val[1]; }; #endif //_SQUSERDATA_H_ ================================================ FILE: squirrel/squtils.h ================================================ /* see copyright notice in squirrel.h */ #ifndef _SQUTILS_H_ #define _SQUTILS_H_ #include typedef struct SQAllocContextT * SQAllocContext; void sq_vm_init_alloc_context(SQAllocContext * ctx); void sq_vm_destroy_alloc_context(SQAllocContext * ctx); void sq_vm_assign_to_alloc_context(SQAllocContext ctx, HSQUIRRELVM vm); void *sq_vm_malloc(SQAllocContext ctx, SQUnsignedInteger size); void *sq_vm_realloc(SQAllocContext ctx, void *p,SQUnsignedInteger oldsize,SQUnsignedInteger size); void sq_vm_free(SQAllocContext ctx, void *p,SQUnsignedInteger size); #define sq_new(__ctx,__ptr,__type, ...) {__ptr=(__type *)sq_vm_malloc((__ctx),sizeof(__type));new (__ptr) __type(__VA_ARGS__);} #define sq_delete(__ctx,__ptr,__type) {__ptr->~__type();sq_vm_free((__ctx),__ptr,sizeof(__type));} #define SQ_MALLOC(__ctx,__size) sq_vm_malloc((__ctx),(__size)); #define SQ_FREE(__ctx,__ptr,__size) sq_vm_free((__ctx),(__ptr),(__size)); #define SQ_REALLOC(__ctx,__ptr,__oldsize,__size) sq_vm_realloc((__ctx),(__ptr),(__oldsize),(__size)); #define sq_aligning(v) (((size_t)(v) + (SQ_ALIGNMENT-1)) & (~(SQ_ALIGNMENT-1))) #ifndef SQ_LIKELY #if (defined(__GNUC__) && (__GNUC__ >= 3)) || defined(__clang__) #if defined(__cplusplus) #define SQ_LIKELY(x) __builtin_expect(!!(x), true) #define SQ_UNLIKELY(x) __builtin_expect(!!(x), false) #else #define SQ_LIKELY(x) __builtin_expect(!!(x), 1) #define SQ_UNLIKELY(x) __builtin_expect(!!(x), 0) #endif #else #define SQ_LIKELY(x) (x) #define SQ_UNLIKELY(x) (x) #endif #endif //sqvector mini vector class, supports objects by value template class sqvector { public: using size_type = SizeT; sqvector(SQAllocContext ctx) : _vals(NULL) , _size(0) , _allocated(0) , _alloc_ctx(ctx) { } sqvector(const sqvector& v) : _vals(NULL) , _size(0) , _allocated(0) , _alloc_ctx(v._alloc_ctx) { copy(v); } void copy(const sqvector& v) { if (_alloc_ctx != v._alloc_ctx) { _releasedata(); _vals = NULL; _allocated = 0; _size = 0; _alloc_ctx = v._alloc_ctx; } else if(_size) { resize(0); //destroys all previous stuff } //resize(v._size); if(v._size > _allocated) { _realloc(v._size); } for(size_type i = 0; i < v._size; i++) { new ((void *)&_vals[i]) T(v._vals[i]); //-V522 } _size = v._size; } ~sqvector() { _releasedata(); } void reserve(size_type newsize) { _realloc(newsize); } void resize(size_type newsize, const T& fill = T()) { if(newsize > _allocated) _realloc(newsize); if(newsize > _size) { while(_size < newsize) { new ((void *)&_vals[_size]) T(fill); _size++; } } else{ for(size_type i = newsize; i < _size; i++) { _vals[i].~T(); } _size = newsize; } } void clear() { resize(0); } void shrinktofit() { if(_size > 4) { _realloc(_size); } } T& top() const { return _vals[_size - 1]; } inline size_type size() const { return _size; } bool empty() const { return (_size <= 0); } inline T &push_back(const T& val = T()) { if(_allocated <= _size) _realloc(_size * 2); return *(new ((void *)&_vals[_size++]) T(val)); } inline T &push_back(T&& val) { if(_allocated <= _size) _realloc(_size * 2); return *(new ((void *)&_vals[_size++]) T(std::move(val))); } inline void pop_back() { _size--; _vals[_size].~T(); } void insert(size_type idx, const T& val) { resize(_size + 1); for(size_type i = _size - 1; i > idx; i--) { _vals[i] = _vals[i - 1]; // -V1002 } _vals[idx] = val; // -V1002 } void remove(size_type idx) { _vals[idx].~T(); if(idx < (_size - 1)) { memmove(&_vals[idx], &_vals[idx+1], sizeof(T) * (_size - idx - 1)); } _size--; } size_type capacity() { return _allocated; } inline T &back() const { return _vals[_size - 1]; } inline T& operator[](size_type pos) const{ return _vals[pos]; } T* _vals; SQAllocContext _alloc_ctx; typedef T* iterator; typedef const T* const_iterator; iterator begin() { return &_vals[0]; } const_iterator begin() const { return &_vals[0]; } iterator end() { return &_vals[_size]; } const_iterator end() const { return &_vals[_size]; } private: void _realloc(size_type newsize) { newsize = (newsize > 0)?newsize:4; _vals = (T*)SQ_REALLOC(_alloc_ctx, _vals, _allocated * sizeof(T), newsize * sizeof(T)); _allocated = newsize; } void _releasedata() { if(_allocated) { for(size_type i = 0; i < _size; i++) _vals[i].~T(); SQ_FREE(_alloc_ctx, _vals, (_allocated * sizeof(T))); } } size_type _size; size_type _allocated; }; #endif //_SQUTILS_H_ ================================================ FILE: squirrel/sqvm.cpp ================================================ /* see copyright notice in squirrel.h */ #include "sqpcheader.h" #include #include #include "opcodes.h" #include "sqvm.h" #include "sqfuncproto.h" #include "sqclosure.h" #include "sqstring.h" #include "sqtable.h" #include "squserdata.h" #include "sqarray.h" #include "sqclass.h" #include "vartrace.h" #include "compiler/sqtypeparser.h" // for sq_stringify_type_mask #include "sq_safe_shift.h" #define TARGET _stkbase[arg0] #define STK(a) _stkbase[(a)] #define RELOAD_STK() _stkbase = _stack._vals + _stackbase #define WATCHDOG_RAREFICATION 8000 static inline void propagate_immutable(const SQObject &obj, SQObject &slot_val) { if (sq_objflags(obj) & SQOBJ_FLAG_IMMUTABLE) slot_val._flags |= SQOBJ_FLAG_IMMUTABLE; } static inline bool check_typemask(SQObjectType tp, SQInteger typemask) { return (tp != OT_NULL) ? (typemask & tp) : (typemask & _RT_NULL); } bool SQVM::BW_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2) { SQInteger res; if((sq_type(o1)|sq_type(o2)) == OT_INTEGER) { SQInteger i1 = _integer(o1), i2 = _integer(o2); switch(op) { case BW_AND: res = i1 & i2; break; case BW_OR: res = i1 | i2; break; case BW_XOR: res = i1 ^ i2; break; case BW_SHIFTL: res = sq_safe_shift_left(i1, i2); break; case BW_SHIFTR: res = sq_safe_shift_right(i1, i2); break; case BW_USHIFTR:res = sq_safe_unsigned_shift_right(i1, i2); break; default: { Raise_Error("internal vm error bitwise op failed"); return false; } } } else { Raise_Error("bitwise op between '%s' and '%s'",GetTypeName(o1),GetTypeName(o2)); return false;} trg = res; return true; } #define _ARITH_(op,trg,o1,o2) \ { \ SQInteger tmask = sq_type(o1)|sq_type(o2); \ switch(tmask) { \ case OT_INTEGER: trg = _integer(o1) op _integer(o2);break; \ case (OT_FLOAT|OT_INTEGER): \ case (OT_FLOAT): trg = tofloat(o1) op tofloat(o2); break; \ default: _GUARD(ARITH_OP((#op)[0],trg,o1,o2)); break;\ } \ } #define _ARITH_NOZERO(op,trg,o1,o2) \ { \ SQInteger tmask = sq_type(o1)|sq_type(o2); \ switch(tmask) { \ case OT_INTEGER: { SQInteger i2 = _integer(o2); \ if (i2 == 0) { Raise_Error("division by zero"); SQ_THROW(); } \ else if (i2 == -1 && _integer(o1) == MIN_SQ_INTEGER) { Raise_Error("integer overflow"); SQ_THROW(); } \ trg = _integer(o1) op i2; } break; \ case (OT_FLOAT|OT_INTEGER): \ case (OT_FLOAT): { SQFloat f2 = tofloat(o2); if(f2 == (SQFloat)0.f) { Raise_Error("float division by zero"); SQ_THROW(); } trg = tofloat(o1) op f2; } break;\ default: _GUARD(ARITH_OP((#op)[0],trg,o1,o2)); break;\ } \ } bool SQVM::ARITH_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2) { SQInteger tmask = sq_type(o1)|sq_type(o2); switch(tmask) { case OT_INTEGER:{ SQInteger res, i1 = _integer(o1), i2 = _integer(o2); switch(op) { case '+': res = i1 + i2; break; case '-': res = i1 - i2; break; case '/': if (i2 == 0) { Raise_Error("integer division by zero"); return false; } else if (i2 == -1 && i1 == MIN_SQ_INTEGER) { Raise_Error("integer overflow"); return false; } res = i1 / i2; break; case '*': res = i1 * i2; break; case '%': if (i2 == 0) { Raise_Error("integer modulo by zero"); return false; } else if (i2 == -1) { res = 0; break; } res = i1 % i2; break; default: res = 0xDEADBEEF; } trg = res; } break; case (OT_FLOAT|OT_INTEGER): case (OT_FLOAT):{ SQFloat res, f1 = tofloat(o1), f2 = tofloat(o2); switch(op) { case '+': res = f1 + f2; break; case '-': res = f1 - f2; break; case '/': if (f2 == 0.f) { Raise_Error("float division by zero"); return false; } res = f1 / f2; break; case '*': res = f1 * f2; break; case '%': if (f2 == 0.f) { Raise_Error("float modulo by zero"); return false; } res = SQFloat(fmod((double)f1,(double)f2)); break; default: res = 0x0f; } trg = res; } break; default: if(op == '+' && (tmask & _RT_STRING)){ if(!StringCat(o1, o2, trg)) return false; } else if(!ArithMetaMethod(op,o1,o2,trg)) { return false; } } return true; } SQVM::SQVM(SQSharedState *ss) : _callstackdata(nullptr), _stack(ss->_alloc_ctx), _etraps(ss->_alloc_ctx) { _sharedstate=ss; _suspended = SQFalse; _suspended_target = -1; _suspended_root = SQFalse; _suspended_traps = -1; _foreignptr = NULL; _nnativecalls = 0; _nmetamethodscall = 0; _lasterror.Null(); _errorhandler.Null(); _debughook = false; _debughook_native = NULL; _debughook_closure.Null(); _compile_line_hook = NULL; _current_thread = -1; _get_current_thread_id_func = NULL; _sq_call_hook = NULL; _watchdog_hook = NULL; _openouters = NULL; ci = NULL; _releasehook = NULL; INIT_CHAIN();ADD_TO_CHAIN(&_ss(this)->_gc_chain,this); } void SQVM::Finalize() { if (_debughook) CallDebugHook('x'); if(_releasehook) { _releasehook(this,_foreignptr,0); _releasehook = NULL; } if(_openouters) CloseOuters(&_stack._vals[0]); _roottable.Null(); _lasterror.Null(); _errorhandler.Null(); _debughook = false; _debughook_native = NULL; _compile_line_hook = NULL; _debughook_closure.Null(); _get_current_thread_id_func = NULL; _sq_call_hook = NULL; _watchdog_hook = NULL; temp_reg.Null(); _callstackdata.resize(0); SQInteger size=_stack.size(); for(SQInteger i=0;i_gc_chain,this); } bool SQVM::ArithMetaMethod(SQInteger op,const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &dest) { SQMetaMethod mm; switch(op){ case '+': mm=MT_ADD; break; case '-': mm=MT_SUB; break; case '/': mm=MT_DIV; break; case '*': mm=MT_MUL; break; case '%': mm=MT_MODULO; break; default: mm = MT_ADD; assert(0); break; //shutup compiler } if(is_delegable(o1) && _delegable(o1)->_delegate) { SQObjectPtr closure; if(_delegable(o1)->GetMetaMethod(this, mm, closure)) { Push(o1);Push(o2); return CallMetaMethod(closure,mm,2,dest); } } Raise_Error("arith op %c on between '%s' and '%s'",(char)op,GetTypeName(o1),GetTypeName(o2)); return false; } bool SQVM::NEG_OP(SQObjectPtr &trg,const SQObjectPtr &o) { switch(sq_type(o)) { case OT_INTEGER: trg = -_integer(o); return true; case OT_FLOAT: trg = -_float(o); return true; case OT_TABLE: case OT_USERDATA: case OT_INSTANCE: if(_delegable(o)->_delegate) { SQObjectPtr closure; if(_delegable(o)->GetMetaMethod(this, MT_UNM, closure)) { Push(o); if(!CallMetaMethod(closure, MT_UNM, 1, temp_reg)) return false; _Swap(trg,temp_reg); return true; } } default:break; //shutup compiler } Raise_Error("attempt to negate a %s", GetTypeName(o)); return false; } #define _RET_SUCCEED(exp) { result = (exp); return true; } bool SQVM::ObjCmp(const SQObjectPtr &o1,const SQObjectPtr &o2,SQInteger &result) { SQObjectType t1 = sq_type(o1), t2 = sq_type(o2); if(t1 == t2) { if(_rawval(o1) == _rawval(o2))_RET_SUCCEED(0); SQObjectPtr res; switch(t1){ case OT_STRING: _RET_SUCCEED(strcmp(_stringval(o1),_stringval(o2))); case OT_INTEGER: case OT_BOOL: _RET_SUCCEED((_integer(o1)<_integer(o2))?-1:1); case OT_FLOAT: _RET_SUCCEED((_float(o1)<_float(o2))?-1:(_float(o1)==_float(o2))?0:1); case OT_TABLE: case OT_USERDATA: case OT_INSTANCE: if(_delegable(o1)->_delegate) { SQObjectPtr closure; if(_delegable(o1)->GetMetaMethod(this, MT_CMP, closure)) { Push(o1);Push(o2); if(CallMetaMethod(closure,MT_CMP,2,res)) { if(sq_type(res) != OT_INTEGER) { Raise_Error("_cmp must return an integer"); return false; } _RET_SUCCEED(_integer(res)) } return false; } } //continues through (no break needed) default: Raise_CompareError(o1, o2); return false; } } else{ if(sq_isnumeric(o1) && sq_isnumeric(o2)){ if((t1==OT_INTEGER) && (t2==OT_FLOAT)) { if( _integer(o1)==_float(o2) ) { _RET_SUCCEED(0); } else if( _integer(o1)<_float(o2) ) { _RET_SUCCEED(-1); } _RET_SUCCEED(1); } else{ if( _float(o1)==_integer(o2) ) { _RET_SUCCEED(0); } else if( _float(o1)<_integer(o2) ) { _RET_SUCCEED(-1); } _RET_SUCCEED(1); } } else if(t1==OT_NULL) {_RET_SUCCEED(-1);} else if(t2==OT_NULL) {_RET_SUCCEED(1);} else { Raise_CompareError(o1,o2); return false; } } } bool SQVM::ObjCmpI(const SQObjectPtr &o1,const SQInteger o2,SQInteger &result) { SQObjectType t1 = sq_type(o1); if(t1 == OT_INTEGER) { SQInteger i1 = _integer(o1); result = (i1 == o2) ? 0 : (i1 < o2 ? -1 : 1); return true; } else if (t1 == OT_FLOAT){ SQFloat f1 = _float(o1); result = (f1 == o2) ? 0 : (f1 < o2 ? -1 : 1); return true; } else if(t1==OT_NULL) {_RET_SUCCEED(-1);} else Raise_CompareError(o1, SQObjectPtr(o2)); return false; } bool SQVM::ObjCmpF(const SQObjectPtr &o1,const SQFloat o2,SQInteger &result) { SQObjectType t1 = sq_type(o1); if(t1 == OT_FLOAT) { float f1 = _float(o1); result = (f1 == o2) ? 0 : (f1 < o2 ? -1 : 1); return true; } else if (t1 == OT_INTEGER){ SQInteger i1 = _integer(o1); result = (i1 == o2) ? 0 : (i1 < o2 ? -1 : 1); return true; } else if(t1==OT_NULL) {_RET_SUCCEED(-1);} else Raise_CompareError(o1, SQObjectPtr(o2)); return false; } bool SQVM::CMP_OP_RESI(CmpOP op, const SQObjectPtr &o1,const SQInteger o2, int &res) { SQInteger r; if(ObjCmpI(o1,o2,r)) { switch(op) { case CMP_G: res = (r > 0); return true; case CMP_GE: res = (r >= 0); return true; case CMP_L: res = (r < 0); return true; case CMP_LE: res = (r <= 0); return true; case CMP_3W: res = r; return true; } assert(0); } return false; } bool SQVM::CMP_OP_RESF(CmpOP op, const SQObjectPtr &o1,const SQFloat o2, int &res) { SQInteger r; if(ObjCmpF(o1,o2,r)) { switch(op) { case CMP_G: res = (r > 0); return true; case CMP_GE: res = (r >= 0); return true; case CMP_L: res = (r < 0); return true; case CMP_LE: res = (r <= 0); return true; case CMP_3W: res = r; return true; } assert(0); } return false; } bool SQVM::CMP_OP_RES(CmpOP op, const SQObjectPtr &o1,const SQObjectPtr &o2, int &res) { SQInteger r; if(ObjCmp(o1,o2,r)) { switch(op) { case CMP_G: res = (r > 0); return true; case CMP_GE: res = (r >= 0); return true; case CMP_L: res = (r < 0); return true; case CMP_LE: res = (r <= 0); return true; case CMP_3W: res = r; return true; } assert(0); } return false; } bool SQVM::CMP_OP(CmpOP op, const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &res) { SQInteger r; if(ObjCmp(o1,o2,r)) { switch(op) { case CMP_G: res = (r > 0); return true; case CMP_GE: res = (r >= 0); return true; case CMP_L: res = (r < 0); return true; case CMP_LE: res = (r <= 0); return true; case CMP_3W: res = r; return true; } assert(0); } return false; } bool SQVM::ToString(const SQObjectPtr &o,SQObjectPtr &res) { char buf[48]; switch(sq_type(o)) { case OT_STRING: res = o; return true; case OT_FLOAT: { int i = scsprintf(buf, sizeof(buf), "%g", _float(o)); for (; i >= 0; i--) if (buf[i] == ',') buf[i] = '.'; } break; case OT_INTEGER: scsprintf(buf, sizeof(buf), _PRINT_INT_FMT, _integer(o)); break; case OT_BOOL: scsprintf(buf, sizeof(buf), _integer(o) ? "true" : "false"); break; case OT_NULL: scsprintf(buf, sizeof(buf), "null"); break; case OT_TABLE: case OT_USERDATA: case OT_INSTANCE: if(_delegable(o)->_delegate) { SQObjectPtr closure; if(_delegable(o)->GetMetaMethod(this, MT_TOSTRING, closure)) { Push(o); if(CallMetaMethod(closure,MT_TOSTRING,1,res)) { if(sq_type(res) == OT_STRING) return true; } else { return false; } } } default: scsprintf(buf, sizeof(buf), "(%s : 0x%p)", GetTypeName(o), (void*)_rawval(o)); } res = SQString::Create(_ss(this), buf); return true; } bool SQVM::StringCat(const SQObjectPtr &str,const SQObjectPtr &obj,SQObjectPtr &dest) { SQObjectPtr a, b; if(!ToString(str, a)) return false; if(!ToString(obj, b)) return false; SQInteger l = _string(a)->_len , ol = _string(b)->_len; char *s = _sp(l + ol + 1); memcpy(s, _stringval(a), l); memcpy(s + l, _stringval(b), ol); dest = SQString::Create(_ss(this), _spval, l + ol); return true; } bool SQVM::TypeOf(const SQObjectPtr &obj1,SQObjectPtr &dest) { if(is_delegable(obj1) && _delegable(obj1)->_delegate) { SQObjectPtr closure; if(_delegable(obj1)->GetMetaMethod(this, MT_TYPEOF, closure)) { Push(obj1); return CallMetaMethod(closure,MT_TYPEOF,1,dest); } } dest = SQString::Create(_ss(this),GetTypeName(obj1)); return true; } bool SQVM::Init(SQVM *friendvm, SQInteger stacksize) { _stack.resize(stacksize + STACK_GROW_THRESHOLD); _alloccallsstacksize = 4; _callstackdata.resize(_alloccallsstacksize); _callsstacksize = 0; _callsstack = &_callstackdata[0]; _stackbase = 0; _top = 0; if(!friendvm) { _roottable = SQTable::Create(_ss(this), 0); sq_base_register(this); } else { _roottable = friendvm->_roottable; _errorhandler = friendvm->_errorhandler; _debughook = friendvm->_debughook; _debughook_native = friendvm->_debughook_native; _debughook_closure = friendvm->_debughook_closure; _compile_line_hook = friendvm->_compile_line_hook; _current_thread = friendvm->_current_thread; _get_current_thread_id_func = friendvm->_get_current_thread_id_func; _sq_call_hook = friendvm->_sq_call_hook; _watchdog_hook = friendvm->_watchdog_hook; } return true; } template bool SQVM::StartCall(SQClosure *closure,SQInteger target,SQInteger args,SQInteger stackbase,bool tailcall) { SQFunctionProto *func = closure->_function; SQInteger paramssize = func->_nparameters; const SQInteger newtop = stackbase + func->_stacksize; SQInteger nargs = args; if(func->_varparams) { paramssize--; if (nargs < paramssize) { Raise_Error("wrong number of parameters passed to '%s' %s:%d (%d passed, at least %d required)", sq_type(func->_name) == OT_STRING ? _stringval(func->_name) : "unknown", sq_type(func->_sourcename) == OT_STRING ? _stringval(func->_sourcename) : "unknown", int(func->_nlineinfos > 0 ? func->_lineinfos->_first_line : 0), (int)nargs, (int)paramssize); return false; } //dumpstack(stackbase); SQInteger nvargs = nargs - paramssize; SQArray *arr = SQArray::Create(_ss(this),nvargs); SQInteger pbase = stackbase+paramssize; SQUnsignedInteger32 mask = func->_param_type_masks ? func->_param_type_masks[paramssize] : ~0u; for(SQInteger n = 0; n < nvargs; n++) { if (!check_typemask(sq_type(_stack._vals[pbase]), mask)) { Raise_ParamTypeError(n + paramssize, mask, sq_type(_stack._vals[pbase]), sq_type(func->_name) == OT_STRING ? _stringval(func->_name) : nullptr); return false; } arr->_values[n] = _stack._vals[pbase]; _stack._vals[pbase].Null(); pbase++; } _stack._vals[stackbase+paramssize] = arr; //dumpstack(stackbase); } else if (paramssize != nargs) { SQInteger ndef = func->_ndefaultparams; SQInteger diff; if(ndef && nargs < paramssize && (diff = paramssize - nargs) <= ndef) { for(SQInteger n = ndef - diff; n < ndef; n++) { _stack._vals[stackbase + (nargs++)] = closure->_defaultparams[n]; } } else { Raise_Error("wrong number of parameters passed to '%s' %s:%d (%d passed, %d required)", sq_type(func->_name) == OT_STRING ? _stringval(func->_name) : "unknown", sq_type(func->_sourcename) == OT_STRING ? _stringval(func->_sourcename) : "unknown", int(func->_nlineinfos > 0 ? func->_lineinfos->_first_line : 0), (int)nargs, (int)paramssize); return false; } } if (SQUnsignedInteger32 *paramTypeMasks = func->_param_type_masks) { SQInteger len = func->_varparams ? paramssize : nargs; for (SQInteger i = 1; i < len; i++) { SQUnsignedInteger32 mask = paramTypeMasks[i]; if (!check_typemask(sq_type(_stack._vals[stackbase + i]), mask)) { Raise_ParamTypeError(i, mask, sq_type(_stack._vals[stackbase + i]), sq_type(func->_name) == OT_STRING ? _stringval(func->_name) : nullptr); return false; } } } if(closure->_env) { _stack._vals[stackbase] = closure->_env->_obj; } if(!EnterFrame(stackbase, newtop, tailcall)) return false; ci->_closure = closure; ci->_literals = func->_literals; ci->_ip = func->_instructions; ci->_target = (SQInt32)target; if constexpr (debughookPresent) { if (_debughook) { CallDebugHook('c'); } } if (closure->_function->_bgenerator) { SQFunctionProto *f = closure->_function; SQGenerator *gen = SQGenerator::Create(_ss(this), closure); if(!gen->Yield(this,f->_stacksize)) return false; SQObjectPtr temp; Return(1, target, temp); _stack._vals[_stackbase + target] = gen; } return true; } template bool SQVM::Return(SQInteger _arg0, SQInteger _arg1, SQObjectPtr &retval) { SQBool _isroot = ci->_root; SQInteger callerbase = _stackbase - ci->_prevstkbase; if constexpr (debughookPresent) { if (_debughook) { for(SQInteger i=0; i_ncalls; i++) { CallDebugHook('r'); } } } SQObjectPtr *dest; if (_isroot) { dest = &(retval); } else if (ci->_target == -1) { dest = NULL; } else { dest = &_stack._vals[callerbase + ci->_target]; } if (dest) { if(_arg0 != 0xFF) { *dest = _stack._vals[_stackbase+_arg1]; } else { dest->Null(); } } LeaveFrame(); return _isroot ? true : false; } #define _RET_ON_FAIL(exp) { if(!exp) return false; } bool SQVM::PLOCAL_INC(SQInteger op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr) { SQObjectPtr trg; _RET_ON_FAIL(ARITH_OP( op , trg, a, incr)); target = a; a = trg; return true; } bool SQVM::DerefInc(SQInteger op,SQObjectPtr &target, SQObjectPtr &self, SQObjectPtr &key, SQObjectPtr &incr, bool postfix) { if (self._flags & SQOBJ_FLAG_IMMUTABLE) { Raise_Error("trying to modify immutable '%s'",GetTypeName(self)); return false; } SQObjectPtr tmp, tself = self, tkey = key; SQObjectPtr *__restrict instanceValue = nullptr; if (sq_type(tself) == OT_INSTANCE) { SQInstance*__restrict instance = _instance(tself); const SQClass *__restrict classType = instance->_class; const SQTable::_HashNode *n = classType->_members->_Get(tkey); if (n && _isfield(n->val)) instanceValue = instance->_values + _member_idx(n->val); else if (n && _isnativefield(n->val)) { // read -> arith -> write back for native fields instance->GetNativeField(_member_idx(n->val), tmp); _RET_ON_FAIL(ARITH_OP(op, target, tmp, incr)) if (!instance->SetNativeField(_member_idx(n->val), target)) { Raise_Error("type mismatch in native field assignment"); return false; } if (postfix) target = tmp; return true; } } else if (sq_type(tself) == OT_TABLE) { SQTable::_HashNode *node = _table(tself)->_Get(tkey); if (node) instanceValue = &node->val; } else if (sq_type(tself) == OT_ARRAY){ if (sq_isnumeric(key)) { SQArray * __restrict array = _array(tself); uint32_t nidx = tointeger(tkey); if (nidx < (SQInteger)array->_values.size()) instanceValue = &array->_values[nidx]; } } if (instanceValue) tmp = _realval(*instanceValue); else { //delegates and OT_USERDATA option. Basically FallbackGet/FallbackSet if (!Get(tself, tkey, tmp, 0)) { return false; } } _RET_ON_FAIL(ARITH_OP( op , target, tmp, incr)) if (instanceValue) *instanceValue = target; else if (!Set(tself, tkey, target)) { return false; } if (postfix) target = tmp; return true; } #define arg0 (_i_._arg0) #define arg1 (_i_._arg1) #define sarg1 (((SQInt32)_i_._arg1)) #define farg1 (_i_._farg1) #define arg2 (_i_._arg2) #define arg3 (_i_._arg3) #define sarg3 ((SQInteger)((signed char)_i_._arg3)) #define sarg0 ((SQInteger)((signed char)_i_._arg0)) SQRESULT SQVM::Suspend() { if (_suspended) return sq_throwerror(this, "cannot suspend an already suspended vm"); if (_nnativecalls!=2) return sq_throwerror(this, "cannot suspend through native calls/metamethods"); return SQ_SUSPEND_FLAG; } #define _FINISH(howmuchtojump) {jump = howmuchtojump; return true; } #define _CHECK_FREEZE() { if (o1._flags & SQOBJ_FLAG_IMMUTABLE) { o2._flags |= SQOBJ_FLAG_IMMUTABLE; o3._flags |= SQOBJ_FLAG_IMMUTABLE; } } bool SQVM::FOREACH_OP(SQObjectPtr &o1,SQObjectPtr &o2,SQObjectPtr &o3,SQObjectPtr &o4,int exitpos,int &jump) { SQInteger nrefidx; switch(sq_type(o1)) { case OT_TABLE: if((nrefidx = _table(o1)->Next(false,o4, o2, o3)) == -1) _FINISH(exitpos); _CHECK_FREEZE(); o4 = (SQInteger)nrefidx; _FINISH(1); case OT_ARRAY: if((nrefidx = _array(o1)->Next(o4, o2, o3)) == -1) _FINISH(exitpos); _CHECK_FREEZE(); o4 = (SQInteger) nrefidx; _FINISH(1); case OT_STRING: if((nrefidx = _string(o1)->Next(o4, o2, o3)) == -1)_FINISH(exitpos); o4 = (SQInteger)nrefidx; _FINISH(1); case OT_CLASS: if((nrefidx = _class(o1)->Next(o4, o2, o3)) == -1)_FINISH(exitpos); _CHECK_FREEZE(); o4 = (SQInteger)nrefidx; _FINISH(1); case OT_USERDATA: case OT_INSTANCE: if(_delegable(o1)->_delegate) { SQObjectPtr itr; SQObjectPtr closure; if(_delegable(o1)->GetMetaMethod(this, MT_NEXTI, closure)) { Push(o1); Push(o4); if(CallMetaMethod(closure, MT_NEXTI, 2, itr)) { o4 = o2 = itr; if(sq_type(itr) == OT_NULL) _FINISH(exitpos); if(!Get(o1, itr, o3, 0)) { Raise_Error("_nexti returned an invalid idx"); return false; } _CHECK_FREEZE(); _FINISH(1); } else { return false; } } if ((nrefidx = (_delegable(o1)->_delegate)->Next(false, o4, o2, o3)) == -1) _FINISH(exitpos); _instance(o1)->Get(o2, o3); _CHECK_FREEZE(); o4 = (SQInteger)nrefidx; _FINISH(1); } break; case OT_GENERATOR: if(_generator(o1)->_state == SQGenerator::eDead) _FINISH(exitpos); if(_generator(o1)->_state == SQGenerator::eSuspended) { SQInteger idx = 0; if(sq_type(o4) == OT_INTEGER) { idx = _integer(o4) + 1; } o2 = idx; o4 = idx; _generator(o1)->Resume(this, o3); _FINISH(0); } default: Raise_Error("cannot iterate %s", GetTypeName(o1)); } return false; } #undef _CHECK_FREEZE #define COND_LITERAL (arg3!=0?ci->_literals[arg1]:STK(arg1)) #define SYNC_IP() do { if (ci) ci->_ip = _ip; } while(0) #define RELOAD_IP() do { _ip = ci->_ip; RELOAD_STK(); } while(0) #define SQ_THROW() { SYNC_IP(); goto exception_trap; } #define _GUARD(exp) { if(!exp) { SQ_THROW();} } bool SQVM::CLOSURE_OP(SQObjectPtr &target, SQFunctionProto *func) { SQInteger nouters; SQClosure *closure = SQClosure::Create(_ss(this), func); if((nouters = func->_noutervalues)) { for(SQInteger i = 0; i_outervalues[i]; switch(v._type){ case otLOCAL: FindOuter(closure->_outervalues[i], &_stack._vals[_stackbase + _integer(v._src)]); break; case otOUTER: closure->_outervalues[i] = _closure(ci->_closure)->_outervalues[_integer(v._src)]; break; } } } SQInteger ndefparams; if((ndefparams = func->_ndefaultparams)) { for(SQInteger i = 0; i < ndefparams; i++) { SQInteger spos = func->_defaultparams[i]; closure->_defaultparams[i] = _stack._vals[_stackbase + spos]; #if SQ_RUNTIME_TYPE_CHECK int nparams = func->_nparameters; int begin = nparams - ndefparams; if (SQUnsignedInteger32 *paramTypeMasks = func->_param_type_masks) if (!check_typemask(sq_type(closure->_defaultparams[i]), paramTypeMasks[begin + i])) { char buf[160]; sq_stringify_type_mask(buf, sizeof(buf), paramTypeMasks[begin + i]); Raise_Error("default param (%d) type '%s' differs from the declared type '%s'", int(i + begin), GetTypeName(closure->_defaultparams[i]), buf); return false; } #endif } } target = closure; return true; } bool SQVM::CLASS_OP(SQObjectPtr &target,SQInteger baseclass) { SQClass *base = NULL; if (baseclass != -1) { if (sq_type(_stack._vals[_stackbase+baseclass]) != OT_CLASS) { Raise_Error("trying to inherit from a %s",GetTypeName(_stack._vals[_stackbase+baseclass])); return false; } base = _class(_stack._vals[_stackbase + baseclass]); } SQClass *cls = SQClass::Create(this, base); if (!cls) return false; target = cls; return true; } bool SQVM::IsEqual(const SQObject &o1,const SQObject &o2) { SQObjectType t1 = sq_type(o1), t2 = sq_type(o2); if(t1 == t2) { if (t1 == OT_FLOAT) return (_float(o1) == _float(o2)); else return (_rawval(o1) == _rawval(o2)); } else { if(sq_isnumeric(o1) && sq_isnumeric(o2)) return (tofloat(o1) == tofloat(o2)); else return false; } } bool SQVM::IsFalse(const SQObject &o) { return _rawval(o) == 0 || (sq_type(o) == OT_FLOAT && _float(o) == SQFloat(0.0)) || (sq_type(o) == OT_INTEGER && _integer(o) == 0); // should be optimized out if _SQ64 && sizeof(SQInteger) == 8 } bool SQVM::IsInstanceOf(const SQObject &obj, const SQClass *cls) { if (sq_type(obj) == OT_INSTANCE) { return _instance(obj)->InstanceOf(cls); } else if (cls->_is_builtin_type) { SQObjectType obj_type = sq_type(obj); // Special handling for closures and native closures - both map to Function class if (cls->_builtin_type_id == OT_CLOSURE && (obj_type == OT_CLOSURE || obj_type == OT_NATIVECLOSURE)) return true; return (obj_type == cls->_builtin_type_id); } else return false; } #if defined(SQ_USED_MEM_COUNTER_DECL) SQ_USED_MEM_COUNTER_DECL #endif #if SQ_WATCHDOG_ENABLED #define SQ_WATCHDOG_CHECK() \ do { \ if (_watchdog_hook && ++watchdogCounter > WATCHDOG_RAREFICATION) { \ watchdogCounter = 0; \ if (!_watchdog_hook(this, false)) { \ Raise_Error("Watchdog: too long execution of quirrel script"); \ SQ_THROW(); \ } \ } \ } while (0) #else #define SQ_WATCHDOG_CHECK() #endif extern SQInstructionDesc g_InstrDesc[]; template bool SQVM::Execute(const SQObjectPtr &closure, SQInteger nargs, SQInteger stackbase,SQObjectPtr &outres, SQBool invoke_err_handler,ExecutionType et) { ValidateThreadAccess(); #if SQ_CHECK_THREAD >= SQ_CHECK_THREAD_LEVEL_FAST if (_get_current_thread_id_func) { if (!_nnativecalls) _current_thread = _get_current_thread_id_func(); else assert(_current_thread == _get_current_thread_id_func()); } #endif SQUnsignedInteger32 watchdogCounter = 0; (void)watchdogCounter; if ((_nnativecalls + 1) > MAX_NATIVE_CALLS) { Raise_Error("Native stack overflow"); return false; } _nnativecalls++; AutoDec ad(&_nnativecalls); SQInteger traps = 0; SQInteger prevci_idx = _callsstacksize; switch(et) { case ET_CALL: { temp_reg = closure; if(!StartCall(_closure(temp_reg), _top - nargs, nargs, stackbase, false)) { //call the handler if there are no calls in the stack, if not relies on the previous node if(ci == NULL) CallErrorHandler(_lasterror); return false; } if(_callsstacksize == prevci_idx) { outres = _stack._vals[_stackbase + (_top-nargs)]; return true; } ci->_root = SQTrue; } break; case ET_RESUME_GENERATOR: if(!_generator(closure)->Resume(this, outres)) { return false; } ci->_root = SQTrue; traps += ci->_etraps; break; case ET_RESUME_VM: case ET_RESUME_THROW_VM: traps = _suspended_traps; ci->_root = _suspended_root; _suspended = SQFalse; if(et == ET_RESUME_THROW_VM) { goto exception_trap; } break; } exception_restore: // { int prevLineNum = -1; int lineHint = 0; SQInstruction *_ip = ci->_ip; SQObjectPtr *_stkbase = _stack._vals + _stackbase; for(;;) { if constexpr (debughookPresent) { if (_debughook) { SQFunctionProto *funcProto = _closure(ci->_closure)->_function; bool isStepPoint; int curLineNum = funcProto->GetLine(_ip, &lineHint, &isStepPoint); if (curLineNum != prevLineNum) { prevLineNum = curLineNum; if (isStepPoint) { SYNC_IP(); CallDebugHook('l', curLineNum); RELOAD_IP(); } } } } const SQInstruction &_i_ = *_ip++; //dumpstack(_stackbase); //printf("\n%s[%d] %s %d %d %d %d\n",_stringval(_closure(ci->_closure)->_function->_name), ci->_ip-1-_closure(ci->_closure)->_function->_instructions,g_InstrDesc[_i_.op].name,arg0,arg1,arg2,arg3); switch(_i_.op) { case _OP_DATA_NOP: continue; case _OP_LOAD: TARGET = ci->_literals[arg1]; continue; case _OP_LOADINT: #ifndef _SQ64 TARGET = (SQInteger)arg1; continue; #else TARGET = (SQInteger)((SQInt32)arg1); continue; #endif case _OP_LOADFLOAT: TARGET = farg1; continue; case _OP_DLOAD: TARGET = ci->_literals[arg1]; STK(arg2) = ci->_literals[arg3];continue; case _OP_TAILCALL:{ SQObjectPtr &t = STK(arg1); if (sq_type(t) == OT_CLOSURE && (!_closure(t)->_function->_bgenerator)){ SQObjectPtr clo = t; SQInteger last_top = _top; if(_openouters) CloseOuters(_stkbase); for (SQInteger i = 0; i < arg3; i++) STK(i) = STK(arg2 + i); SYNC_IP(); _GUARD(StartCall(_closure(clo), ci->_target, arg3, _stackbase, true)); if (last_top >= _top) { _top = last_top; } RELOAD_IP(); continue; } } case _OP_CALL: case _OP_NULLCALL: { SQObjectPtr clo = STK(arg1); int tgt0 = arg0 == 255 ? -1 : arg0; bool nullcall = (_i_.op == _OP_NULLCALL); switch (sq_type(clo)) { case OT_CLOSURE: SYNC_IP(); _GUARD(StartCall(_closure(clo), tgt0, arg3, _stackbase+arg2, false)); RELOAD_IP(); continue; case OT_NATIVECLOSURE: { bool suspend; bool tailcall; SYNC_IP(); _GUARD(CallNative(_nativeclosure(clo), arg3, _stackbase+arg2, clo, tgt0, suspend, tailcall)); RELOAD_STK(); if(suspend){ _suspended = SQTrue; _suspended_target = tgt0; _suspended_root = ci->_root; _suspended_traps = traps; outres = clo; SYNC_IP(); return true; } if(tgt0 != -1 && !tailcall) { STK(tgt0) = clo; } RELOAD_IP(); } continue; case OT_CLASS:{ SQClass *theclass = _class(clo); if (theclass->_is_builtin_type) { // For built-in types, call the constructor directly (no instance creation) SQObjectPtr ctor; if (!theclass->GetConstructor(ctor)) { Raise_Error("built-in type '%s' has no constructor", IdType2Name(theclass->_builtin_type_id)); SQ_THROW(); } assert(sq_type(ctor) == OT_NATIVECLOSURE); SQInteger stkbase = _stackbase+arg2; _stack._vals[stkbase].Null(); // 'this' bool dummy; SYNC_IP(); _GUARD(CallNative(_nativeclosure(ctor), arg3, stkbase, ctor, tgt0, dummy, dummy)); RELOAD_STK(); if (tgt0 != -1) { STK(tgt0) = ctor; } } else { // Regular script class - create instance then call constructor SQObjectPtr inst, ctor; _GUARD(CreateClassInstance(theclass,inst,ctor)); if(tgt0 != -1) { STK(tgt0) = inst; } SQInteger stkbase; switch(sq_type(ctor)) { case OT_CLOSURE: stkbase = _stackbase+arg2; _stack._vals[stkbase] = inst; SYNC_IP(); _GUARD(StartCall(_closure(ctor), -1, arg3, stkbase, false)); RELOAD_IP(); break; case OT_NATIVECLOSURE: bool dummy; stkbase = _stackbase+arg2; _stack._vals[stkbase] = inst; SYNC_IP(); _GUARD(CallNative(_nativeclosure(ctor), arg3, stkbase, ctor, -1, dummy, dummy)); RELOAD_STK(); break; case OT_NULL: if (arg3 > 1) { Raise_Error("cannot call default constructor with arguments"); SQ_THROW(); } break; default: // cannot happen, but still... assert(!"invalid constructor type"); Raise_Error("invalid constructor type %s", IdType2Name(sq_type(ctor))); SQ_THROW(); break; } } } break; case OT_TABLE: case OT_USERDATA: case OT_INSTANCE:{ SQObjectPtr mmclosure; if(_delegable(clo)->_delegate && _delegable(clo)->GetMetaMethod(this,MT_CALL,mmclosure)) { Push(clo); for (SQInteger i = 0; i < arg3; i++) Push(STK(arg2 + i)); if(!CallMetaMethod(mmclosure, MT_CALL, arg3+1, clo)) SQ_THROW(); RELOAD_STK(); if(tgt0 != -1) { STK(tgt0) = clo; } break; } //Raise_Error("attempt to call '%s'", GetTypeName(clo)); //SQ_THROW(); } default: if (nullcall && sq_type(clo)==OT_NULL) { if(tgt0 != -1) { STK(tgt0).Null(); } } else { Raise_Error("attempt to call '%s'", GetTypeName(clo)); SQ_THROW(); } } } continue; case _OP_PREPCALL: case _OP_PREPCALLK: { SQObjectPtr &key = _i_.op == _OP_PREPCALLK?(ci->_literals)[arg1]:STK(arg1); SQObjectPtr &o = STK(arg2); if (!Get(o, key, temp_reg,0)) { SQ_THROW(); } STK(arg3) = o; _Swap(TARGET,temp_reg);//TARGET = temp_reg; } continue; case _OP_GETK:{ SQUnsignedInteger getFlagsByOp = (arg3 & OP_GET_FLAG_ALLOW_TYPE_METHODS) ? 0 : GET_FLAG_NO_TYPE_METHODS; if (arg3 & OP_GET_FLAG_TYPE_METHODS_ONLY) getFlagsByOp |= GET_FLAG_TYPE_METHODS_ONLY; if (arg3 & OP_GET_FLAG_NO_ERROR) { SQInteger fb = GetImpl(STK(arg2), ci->_literals[arg1], temp_reg, GET_FLAG_DO_NOT_RAISE_ERROR | getFlagsByOp); if (fb == SLOT_STATUS_OK) { _Swap(TARGET,temp_reg);//TARGET = temp_reg; } else if (fb == SLOT_STATUS_ERROR) { SQ_THROW(); } else if (!(arg3 & OP_GET_FLAG_KEEP_VAL)) { TARGET.Null(); } } else { if (!Get(STK(arg2), ci->_literals[arg1], temp_reg, getFlagsByOp)) { SQ_THROW(); } _Swap(TARGET,temp_reg);//TARGET = temp_reg; } continue; } case _OP_MOVE: TARGET = STK(arg1); continue; case _OP_NEWSLOT: _GUARD(NewSlot(STK(arg2), STK(arg1), STK(arg3), false)); if(arg0 != 0xFF) TARGET = STK(arg3); continue; case _OP_NEWSLOTK: _GUARD(NewSlot(STK(arg2), ci->_literals[arg1], STK(arg3), false)); if(arg0 != 0xFF) TARGET = STK(arg3); continue; case _OP_DELETE: _GUARD(DeleteSlot(STK(arg1), STK(arg2), TARGET)); continue; case _OP_SET_LITERAL: { uint64_t *__restrict hintP = ((uint64_t*__restrict )(_ip++)); //-V1032 uint64_t hint = *hintP; const SQObjectPtr &from = STK(arg2), &__restrict key = ci->_literals[arg1], &val = STK(arg3); auto sqType = sq_type(from); if (sqType == OT_INSTANCE && !(from._flags & SQOBJ_FLAG_IMMUTABLE))//for wrong access go to normal Set { SQInstance *__restrict instance = _instance(from); const SQClass *__restrict classType = instance->_class; uint32_t memberIdx; //todo:key is string literal, so we better store it's index in literal, or it's hash, rather than use SQObjectPtr from generated previous LOAD command const SQTable *__restrict members = classType->_members; //some class ID. Ideally it is 32bit key, which is correct only when locked //we can achieve that. When unlocked - class has _locked == 0. When _locked != 0, it is class Index+1 in VM (can be just some hash_set, or even just 32 bit hash from pointer) //however, right now we rely on 40 bit hint - which is class pointer. still working good const uint64_t classTypeId = classType->lockedTypeId(); if (SQ_LIKELY(classTypeId && SQClass::classTypeFromHint(hint) == classTypeId)) { memberIdx = uint32_t(hint>>uintptr_t(SQClass::CLASS_BITS)); //todo: validate cache in debug build! //val = hintedMemberIdx ? members->_nodex + hintedMemberIdx-1 : nullptr; } else { if (!members->GetStrToInt(key, memberIdx)) { memberIdx = 0u; } else { uint32_t kind = memberIdx & MEMBER_KIND_MASK; if (kind != MEMBER_TYPE_FIELD && kind != MEMBER_TYPE_NATIVE_FIELD) memberIdx = 0u; } //store hint back *hintP = ((uint64_t(memberIdx)<SetMemberField(memberIdx, val); else if (SQ_UNLIKELY(!instance->SetNativeField(_member_idxi(memberIdx), val))) { Raise_Error("type mismatch in native field assignment"); SQ_THROW(); } } else { SQInteger fb = FallBackSet(from,key,val); if (SQ_UNLIKELY(fb != SLOT_STATUS_OK)) { if (fb == SLOT_STATUS_NO_MATCH) Raise_IdxError(key); SQ_THROW(); } } } else if (sqType == OT_TABLE && !(from._flags & SQOBJ_FLAG_IMMUTABLE))//for wrong access go to normal Set { SQTable *__restrict tbl = _table(from); uint64_t cid = tbl->_classTypeId; SQTable::_HashNode *node = nullptr; if (SQ_LIKELY(cid)) { if (SQ_LIKELY((cid & TBL_CLASS_CLASS_MASK) == (hint & TBL_CLASS_CLASS_MASK))) { node = tbl->GetNodeFromTypeHint(hint, key); } else { node = tbl->_GetStr(_rawval(key), _string(key)->_hash & tbl->_numofnodes_minus_one); if (SQ_LIKELY(node)) { size_t nodeIdx = node - tbl->_nodes; assert(nodeIdx <= TBL_CLASS_TYPE_MEMBER_MASK); *hintP = ((cid & TBL_CLASS_CLASS_MASK) | nodeIdx); } } } if (node) { node->val = val; } else { // fallback no unoptimized version if (!Set(from, key, val)) { SQ_THROW(); } } } else // default implementation { if (!Set(from, key, val)) { SQ_THROW(); } } if (arg0 != 0xFF) TARGET = val; continue; } case _OP_SET: if (!Set(STK(arg2), STK(arg1), STK(arg3))) { SQ_THROW(); } if (arg0 != 0xFF) TARGET = STK(arg3); continue; case _OP_SETI: case _OP_SETK: if (!Set(STK(arg2), _i_.op == _OP_SETI ? SQObjectPtr(SQInteger(arg1)) : ci->_literals[arg1], STK(arg3))) { SQ_THROW(); } if (arg0 != 0xFF) TARGET = STK(arg3); continue; case _OP_GET_LITERAL:{ uint64_t *__restrict hintP = ((uint64_t*__restrict )(_ip++)); //-V1032 uint64_t hint = *hintP; const SQUnsignedInteger getFlagsByOp = GET_FLAG_NO_TYPE_METHODS; const SQObjectPtr &__restrict from = STK(arg2), &__restrict key = ci->_literals[arg1]; auto sqType = sq_type(from); //Or we can disallow table access by table.key if (sqType == OT_INSTANCE) { const SQInstance *__restrict instance = _instance(from); const SQClass *__restrict classType = instance->_class; uint32_t memberIdx; //todo:key is string literal, so we better store it's index in literal, or it's hash, rather than use SQObjectPtr from generated previous LOAD command const SQTable *__restrict members = classType->_members; //some class ID. Ideally it is 32bit key, which is correct only when locked //we can achieve that. When unlocked - class has _locked == 0. When _locked != 0, it is class Index+1 in VM (can be just some hash_set, or even just 32 bit hash from pointer) //however, right now we rely on 40 bit hint - which is class pointer. still working good const uint64_t classTypeId = classType->lockedTypeId(); if (SQ_LIKELY(classTypeId && SQClass::classTypeFromHint(hint) == classTypeId)) { memberIdx = uint32_t(hint>>uintptr_t(SQClass::CLASS_BITS)); //todo: validate cache in debug build! //val = hintedMemberIdx ? members->_nodex + hintedMemberIdx-1 : nullptr; } else { //this is optimized version, can be just memberIdx = members->Get(key, tmp_reg) ? _integer(tmp_reg) : 0u; if (!members->GetStrToInt(key, memberIdx)) memberIdx = 0u; //store hint back *hintP = ((uint64_t(memberIdx)<GetMember(memberIdx, temp_reg); } else { SQInteger fb = FallBackGet(from,key,temp_reg); if (SQ_UNLIKELY(fb != SLOT_STATUS_OK)) { if (fb == SLOT_STATUS_NO_MATCH) Raise_IdxError(key); SQ_THROW(); } } propagate_immutable(from, temp_reg); } else if (sqType == OT_TABLE) { SQTable *__restrict tbl = _table(from); uint64_t cid = tbl->_classTypeId; SQTable::_HashNode *node = nullptr; if (SQ_LIKELY(cid)) { if (SQ_LIKELY((cid & TBL_CLASS_CLASS_MASK) == (hint & TBL_CLASS_CLASS_MASK))) { node = tbl->GetNodeFromTypeHint(hint, key); } else { node = tbl->_GetStr(_rawval(key), _string(key)->_hash & tbl->_numofnodes_minus_one); if (SQ_LIKELY(node)) { size_t nodeIdx = node - tbl->_nodes; assert(nodeIdx <= TBL_CLASS_TYPE_MEMBER_MASK); *hintP = ((cid & TBL_CLASS_CLASS_MASK) | nodeIdx); } } } if (node) { temp_reg = _realval(node->val); propagate_immutable(from, temp_reg); } else { // fallback no unoptimized version if (!Get(from, key, temp_reg, getFlagsByOp)) { SQ_THROW(); } } } else { if (!Get(from, key, temp_reg, getFlagsByOp)) { SQ_THROW(); } } _Swap(TARGET,temp_reg);//TARGET = temp_reg; continue; } case _OP_GET:{ SQUnsignedInteger getFlagsByOp = (arg3 & OP_GET_FLAG_ALLOW_TYPE_METHODS) ? 0 : GET_FLAG_NO_TYPE_METHODS; if (arg3 & OP_GET_FLAG_TYPE_METHODS_ONLY) getFlagsByOp |= GET_FLAG_TYPE_METHODS_ONLY; if (arg3 & OP_GET_FLAG_NO_ERROR) { SQInteger fb = GetImpl(STK(arg2), STK(arg1), temp_reg, GET_FLAG_DO_NOT_RAISE_ERROR | getFlagsByOp); if (fb == SLOT_STATUS_OK) { _Swap(TARGET,temp_reg); } else if (fb == SLOT_STATUS_ERROR) { SQ_THROW(); } else if (!(arg3 & OP_GET_FLAG_KEEP_VAL)) { TARGET.Null(); } } else { if (!Get(STK(arg2), STK(arg1), temp_reg, getFlagsByOp)) { SQ_THROW(); } _Swap(TARGET,temp_reg);//TARGET = temp_reg; } continue; } case _OP_EQ: TARGET = IsEqual(STK(arg2),COND_LITERAL); continue; case _OP_NE: TARGET = !IsEqual(STK(arg2),COND_LITERAL); continue; case _OP_ADD: _ARITH_(+,TARGET,STK(arg2),STK(arg1)); continue; case _OP_ADDI: {SQObjectPtr ai((SQInteger)arg1); _ARITH_(+,TARGET,STK(arg2), ai); continue;} case _OP_SUB: _ARITH_(-,TARGET,STK(arg2),STK(arg1)); continue; case _OP_MUL: _ARITH_(*,TARGET,STK(arg2),STK(arg1)); continue; case _OP_DIV: _ARITH_NOZERO(/,TARGET,STK(arg2),STK(arg1)); continue; case _OP_MOD: _GUARD(ARITH_OP('%',TARGET,STK(arg2),STK(arg1))); continue; case _OP_BITW: _GUARD(BW_OP( arg3,TARGET,STK(arg2),STK(arg1))); continue; case _OP_RETURN: SQ_WATCHDOG_CHECK(); if((ci)->_generator) { (ci)->_generator->Kill(); } if(Return(arg0, arg1, temp_reg)){ assert(traps==0); //outres = temp_reg; _Swap(outres,temp_reg); return true; } RELOAD_IP(); continue; case _OP_LOADNULLS:{ SQInt32 n=arg1-1; assert(n>=0); do { STK(arg0+n).Null(); } while (--n >= 0); }continue; case _OP_LOADROOT: TARGET = _roottable; continue; case _OP_LOADBOOL: TARGET = arg1?true:false; continue; case _OP_LOADCALLEE: TARGET = ci->_closure; if (arg2) { STK(arg2) = STK(arg3); } continue; case _OP_DMOVE: STK(arg0) = STK(arg1); STK(arg2) = STK(arg3); continue; case _OP_JMP: _ip += sarg1; continue; case _OP_JCMP: { int r; const uint8_t uArg3 = arg3; _GUARD(CMP_OP_RES((CmpOP)(uArg3&7),STK(arg2),STK(arg0),r)); if(uint8_t(bool(r)) == (uArg3>>3)) { SQ_WATCHDOG_CHECK(); _ip+=(sarg1); } } continue; case _OP_JCMPK: { int r; const uint8_t uArg3 = arg3; _GUARD(CMP_OP_RES((CmpOP)(uArg3&7),STK(arg2),ci->_literals[arg0],r)); if(uint8_t(bool(r)) == (uArg3>>3)) { SQ_WATCHDOG_CHECK(); _ip+=(sarg1); } } continue; case _OP_JCMPI: { int r; //todo: optimize comparison with int const uint8_t uArg3 = arg3; _GUARD(CMP_OP_RESI((CmpOP)(uArg3&7),STK(arg2), (SQInteger)sarg1, r)); if(uint8_t(bool(r)) == (uArg3>>3)) { SQ_WATCHDOG_CHECK(); _ip+=(sarg0); } } continue; case _OP_JCMPF: { int r; //todo: optimize comparison with float const uint8_t uArg3 = arg3; _GUARD(CMP_OP_RESF((CmpOP)(uArg3&7),STK(arg2), farg1, r)); if(uint8_t(bool(r)) == (uArg3>>3)) { SQ_WATCHDOG_CHECK(); _ip+=(sarg0); } } continue; case _OP_JZ: { SQ_WATCHDOG_CHECK(); if(uint8_t(IsFalse(STK(arg0))) != arg2) { _ip+=(sarg1); } } continue; case _OP_GETOUTER: { SQClosure *cur_cls = _closure(ci->_closure); SQOuter *otr = _outer(cur_cls->_outervalues[arg1]); TARGET = *(otr->_valptr); if (arg2) STK(arg2) = STK(arg3); } continue; case _OP_SETOUTER: { SQClosure *cur_cls = _closure(ci->_closure); SQOuter *otr = _outer(cur_cls->_outervalues[arg1]); *(otr->_valptr) = STK(arg2); if(arg0 != 0xFF) { TARGET = STK(arg2); } } continue; case _OP_NEWOBJ: switch(arg3) { case NEWOBJ_TABLE: TARGET = SQTable::Create(_ss(this), arg1 ? arg1 + 1 : 0); continue; case NEWOBJ_ARRAY: TARGET = SQArray::Create(_ss(this), 0); _array(TARGET)->Reserve(arg1); continue; case NEWOBJ_CLASS: _GUARD(CLASS_OP(TARGET,arg1)); continue; default: assert(0); continue; } case _OP_APPENDARRAY: { // No need to check for immutability here since it is only used for array initialization SQObject val; val._unVal.raw = 0; val._flags = 0; switch(arg2) { case AAT_STACK: val = STK(arg1); break; case AAT_LITERAL: val = ci->_literals[arg1]; break; case AAT_INT: val._type = OT_INTEGER; #ifndef _SQ64 val._unVal.nInteger = (SQInteger)arg1; #else val._unVal.nInteger = (SQInteger)((SQInt32)arg1); #endif break; case AAT_FLOAT: val._type = OT_FLOAT; val._unVal.fFloat = *((const SQFloat *)&arg1); break; case AAT_BOOL: val._type = OT_BOOL; val._unVal.nInteger = arg1; break; default: val._type = OT_INTEGER; assert(0); break; } _array(STK(arg0))->Append(val); continue; } case _OP_COMPARITH: case _OP_COMPARITH_K: { SQInteger selfidx = (((SQUnsignedInteger)arg1&0xFFFF0000)>>16); _GUARD(DerefInc(arg3, TARGET, STK(selfidx), _i_.op == _OP_COMPARITH_K ? ci->_literals[arg2] : STK(arg2), STK(arg1&0x0000FFFF), false)); } continue; case _OP_INC: { SQObjectPtr o(sarg3); _GUARD(DerefInc('+',TARGET, STK(arg1), STK(arg2), o, false)); } continue; case _OP_INCL: { SQObjectPtr &a = STK(arg1); if(sq_type(a) == OT_INTEGER) { a._unVal.nInteger = _integer(a) + sarg3; } else { SQObjectPtr o(sarg3); _ARITH_(+,a,a,o); } } continue; case _OP_PINC: { SQObjectPtr o(sarg3); _GUARD(DerefInc('+',TARGET, STK(arg1), STK(arg2), o, true)); } continue; case _OP_PINCL: { SQObjectPtr &a = STK(arg1); if(sq_type(a) == OT_INTEGER) { TARGET = a; a._unVal.nInteger = _integer(a) + sarg3; } else { SQObjectPtr o(sarg3); _GUARD(PLOCAL_INC('+',TARGET, STK(arg1), o)); } } continue; case _OP_CMP: _GUARD(CMP_OP((CmpOP)arg3,STK(arg2),STK(arg1),TARGET)) continue; case _OP_EXISTS: TARGET = Get(STK(arg1), STK(arg2), temp_reg, GET_FLAG_DO_NOT_RAISE_ERROR | GET_FLAG_NO_TYPE_METHODS); continue; case _OP_INSTANCEOF: if (sq_type(STK(arg1)) != OT_CLASS) { Raise_Error("cannot apply instanceof between a %s and a %s",GetTypeName(STK(arg1)),GetTypeName(STK(arg2))); SQ_THROW(); } TARGET = IsInstanceOf(STK(arg2), _class(STK(arg1))); continue; case _OP_AND:{ if(IsFalse(STK(arg2))) { TARGET = STK(arg2); _ip += (sarg1); } } continue; case _OP_OR:{ if(!IsFalse(STK(arg2))) { TARGET = STK(arg2); _ip += (sarg1); } } continue; case _OP_NULLCOALESCE: if (!sq_isnull(STK(arg2))) { TARGET = STK(arg2); _ip += (sarg1); } continue; case _OP_NEG: _GUARD(NEG_OP(TARGET,STK(arg1))); continue; case _OP_NOT:{ TARGET = IsFalse(STK(arg1)); } continue; case _OP_BWNOT: if(sq_type(STK(arg1)) == OT_INTEGER) { SQInteger t = _integer(STK(arg1)); TARGET = SQInteger(~t); continue; } Raise_Error("attempt to perform a bitwise op on a %s", GetTypeName(STK(arg1))); SQ_THROW(); case _OP_CLOSURE: { SQClosure *c = ci->_closure._unVal.pClosure; SQFunctionProto *fp = c->_function; if(!CLOSURE_OP(TARGET,fp->_functions[arg1]._unVal.pFunctionProto)) { SQ_THROW(); } continue; } case _OP_YIELD:{ if(ci->_generator) { if(sarg1 != MAX_FUNC_STACKSIZE) temp_reg = STK(arg1); if (_openouters) CloseOuters(_stkbase); SYNC_IP(); _GUARD(ci->_generator->Yield(this,arg2)); traps -= ci->_etraps; if(sarg1 != MAX_FUNC_STACKSIZE) _Swap(STK(arg1),temp_reg);//STK(arg1) = temp_reg; } else { Raise_Error("trying to yield a '%s', only generator can be yielded", GetTypeName(ci->_closure)); SQ_THROW();} if(Return(arg0, arg1, temp_reg)){ assert(traps == 0); outres = temp_reg; return true; } RELOAD_IP(); } continue; case _OP_RESUME: if(sq_type(STK(arg1)) != OT_GENERATOR){ Raise_Error("trying to resume a '%s',only genenerator can be resumed", GetTypeName(STK(arg1))); SQ_THROW();} SYNC_IP(); _GUARD(_generator(STK(arg1))->Resume(this, TARGET)); traps += ci->_etraps; RELOAD_IP(); continue; case _OP_PREFOREACH:{ STK(arg2).Null();STK(arg2+1).Null();STK(arg2+2).Null(); auto &arg0Stack = STK(arg0); int tojump; SYNC_IP(); _GUARD(FOREACH_OP(arg0Stack,STK(arg2),STK(arg2+1),STK(arg2+2),2,tojump)); RELOAD_IP(); if (tojump == 1) _ip ++; else if (tojump == 2) // empty _ip += sarg1; } continue; case _OP_POSTFOREACH: assert(sq_type(STK(arg0)) == OT_GENERATOR); if(_generator(STK(arg0))->_state == SQGenerator::eDead) _ip += sarg1; continue; case _OP_FOREACH:{ const int jumpToBodyOffset = sarg1; auto &arg0Stack = STK(arg0); const bool isGenerator = sq_type(arg0Stack) == OT_GENERATOR; if (isGenerator) _ip += jumpToBodyOffset - 1; // so generator will resume at postforeach SYNC_IP(); int tojump; _GUARD(FOREACH_OP(arg0Stack,STK(arg2),STK(arg2+1),STK(arg2+2),2,tojump)); RELOAD_IP(); assert((tojump == 0 && isGenerator) || (tojump != 0 && !isGenerator)); if (tojump == 1) _ip += jumpToBodyOffset; }continue; case _OP_CLONE: _GUARD(Clone(STK(arg1), TARGET)); continue; case _OP_TYPEOF: _GUARD(TypeOf(STK(arg1), TARGET)) continue; case _OP_PUSHTRAP:{ _etraps.push_back(SQExceptionTrap(_top,_stackbase, _ip+arg1, arg0)); traps++; ci->_etraps++; } continue; case _OP_POPTRAP: { for(SQInteger i = 0; i < arg0; i++) { _etraps.pop_back(); traps--; ci->_etraps--; } } continue; case _OP_THROW: Raise_Error(TARGET); SQ_THROW(); continue; case _OP_NEWSLOTA: _GUARD(NewSlot(STK(arg1),STK(arg2),STK(arg3),(arg0&NEW_SLOT_STATIC_FLAG)?true:false)); continue; case _OP_GETBASE:{ SQClosure *clo = _closure(ci->_closure); if(clo->_base) { TARGET = clo->_base; } else { TARGET.Null(); } continue; } case _OP_CLOSE: if(_openouters) CloseOuters(&(STK(arg1))); continue; case _OP_PATCH_DOCOBJ: { SQObjectPtr &o = TARGET; SQObjectPtr findKey; SQObjectPtr foundValue; findKey._unVal.raw = arg1; findKey._type = OT_USERPOINTER; SQTable * tbl = _table(_sharedstate->doc_objects); if (tbl->Get(findKey, foundValue)) { SQObjectPtr replaceWithKey; replaceWithKey._unVal.pUserPointer = o._unVal.pUserPointer; replaceWithKey._type = OT_USERPOINTER; tbl->NewSlot(replaceWithKey, foundValue); tbl->Remove(findKey); } continue; } case _OP_LOAD_STATIC_MEMO: // _staticmemos[arg1] -> STK(arg0), jump to ((arg2 << 8) + arg3) STK(arg0) = _closure(ci->_closure)->_function->_staticmemos[arg1]; _ip += (arg2 << 8) + arg3; //-V595 continue; case _OP_SAVE_STATIC_MEMO: { // STK(arg0) -> _staticmemos[arg1], STK(arg0) -> STK(loadInstr->_arg0), // modify instruction op at -((arg2 << 8) + arg3) to _OP_LOAD_STATIC_MEMO SQObjectPtr & staticmemo = STK(arg0); SQObjectType tp = sq_type(staticmemo); bool isAutoMemo = (arg1 & STATIC_MEMO_AUTO_FLAG) != 0; SQInteger staticIdx = arg1 & STATIC_MEMO_IDX_MASK; if (isAutoMemo && (tp == OT_ARRAY || tp == OT_TABLE || tp == OT_INSTANCE || tp == OT_CLASS)) { // don't auto-memoize mutable containers - might change observable behavior // but still copy result to the LOAD instruction's target register if needed SQInstruction * loadInstr = (_ip - (arg2 << 8) - arg3); if (loadInstr->_arg0 != arg0) STK(loadInstr->_arg0) = staticmemo; continue; } staticmemo._flags |= SQOBJ_FLAG_IMMUTABLE; SQObjectPtr & storedStatic = _closure(ci->_closure)->_function->_staticmemos[staticIdx]; //-V595 storedStatic = staticmemo; if (ISREFCOUNTED(tp)) { #ifdef NO_GARBAGE_COLLECTOR __AddRef(storedStatic._type, storedStatic._unVal); #else _ss(this)->_refs_table.AddRef(storedStatic); #endif } SQInstruction * loadInstr = (_ip - (arg2 << 8) - arg3); //-V595 if (loadInstr->_arg0 != arg0) STK(loadInstr->_arg0) = staticmemo; loadInstr->_arg1 = staticIdx; // strip STATIC_MEMO_AUTO_FLAG loadInstr->op = _OP_LOAD_STATIC_MEMO; continue; } case _OP_FREEZE: STK(arg1)._flags |= SQOBJ_FLAG_IMMUTABLE; TARGET = STK(arg1); continue; case _OP_CHECK_TYPE: if (!check_typemask(sq_type(STK(arg0)), arg1)) { char buf[160]; sq_stringify_type_mask(buf, sizeof(buf), arg1); Raise_Error("type '%s' differs from the declared type '%s'", GetTypeName(STK(arg0)), buf); SQ_THROW(); } //TARGET = STK(arg2); continue; } } } exception_trap: { SQObjectPtr currerror = _lasterror; // dumpstack(_stackbase); SQInteger last_top = _top; if ((_ss(this)->_notifyallexceptions || (!traps && invoke_err_handler)) && !sq_isnull(currerror)) CallErrorHandler(currerror); while( ci ) { if(ci->_etraps > 0) { SQExceptionTrap &et = _etraps.top(); ci->_ip = et._ip; _top = et._stacksize; _stackbase = et._stackbase; _stack._vals[_stackbase + et._extarget] = currerror; _etraps.pop_back(); traps--; ci->_etraps--; while(last_top >= _top) _stack._vals[last_top--].Null(); goto exception_restore; } else if (_debughook) { //notify debugger of a "return" //even if it really an exception unwinding the stack for(SQInteger i = 0; i < ci->_ncalls; i++) { CallDebugHook('r'); } } if(ci->_generator) ci->_generator->Kill(); bool mustbreak = ci && ci->_root; LeaveFrame(); if(mustbreak) break; } _lasterror = currerror; return false; } assert(0); return false; } bool SQVM::CreateClassInstance(SQClass *theclass, SQObjectPtr &__restrict out_inst_res, SQObjectPtr &__restrict constructor) { SQInstance *inst = theclass->CreateInstance(this); if (!inst) return false; out_inst_res = inst; if (!theclass->GetConstructor(constructor)) { constructor.Null(); } return true; } void SQVM::CallErrorHandler(const SQObjectPtr &error) { if (ci) ci->_ip--; if (_debughook_native && ci) { SQObjectPtr errStrHolder; const char *errStr; if (sq_type(error) == OT_STRING) { errStr = _stringval(error); } else { errStrHolder = PrintObjVal(error); errStr = _stringval(errStrHolder); } if (ci->_closure._type == OT_NATIVECLOSURE) { _debughook_native(this, 'e', "", 0, errStr); } else { SQFunctionProto *func = _closure(ci->_closure)->_function; if (func) { const char *src = sq_type(func->_sourcename) == OT_STRING ? _stringval(func->_sourcename) : NULL; SQInteger line = func->GetLine(ci->_ip); _debughook_native(this, 'e', src, line, errStr); } } } if(sq_type(_errorhandler) != OT_NULL) { SQObjectPtr out; assert(_top+2 <= _stack.size()); Push(_roottable); Push(error); Call(_errorhandler, 2, _top-2, out,SQFalse); Pop(2); } if (ci) ci->_ip++; } void SQVM::CallDebugHook(SQInteger type,SQInteger forcedline) { _debughook = false; if (!ci) { if (_debughook_native) _debughook_native(this, type, "", -1, ""); else { SQObjectPtr temp_reg; // -V688 SQInteger nparams = 5; Push(_roottable); Push(SQObjectPtr(type)); Push(SQObjectPtr(SQString::Create(_ss(this), ""))); Push(SQObjectPtr(SQInteger(-1))); Push(SQObjectPtr(SQString::Create(_ss(this), ""))); Call(_debughook_closure, nparams, _top - nparams, temp_reg, SQFalse); Pop(nparams); } _debughook = true; return; } SQFunctionProto *func=_closure(ci->_closure)->_function; if(_debughook_native) { const char *src = sq_type(func->_sourcename) == OT_STRING?_stringval(func->_sourcename):NULL; const char *fname = sq_type(func->_name) == OT_STRING?_stringval(func->_name):NULL; SQInteger line = forcedline?forcedline:func->GetLine(ci->_ip); _debughook_native(this,type,src,line,fname); } else { SQObjectPtr temp_reg; SQInteger nparams=5; Push(_roottable); Push(SQObjectPtr(type)); Push(func->_sourcename); Push(SQObjectPtr(forcedline ? forcedline : func->GetLine(ci->_ip))); Push(func->_name); Call(_debughook_closure,nparams,_top-nparams,temp_reg,SQFalse); Pop(nparams); } _debughook = true; } bool SQVM::CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newbase, SQObjectPtr &retval, SQInt32 target,bool &suspend, bool &tailcall) { SQInteger nparamscheck = nclosure->_nparamscheck; SQInteger newtop = newbase + nargs + nclosure->_noutervalues; if (_nnativecalls + 1 > MAX_NATIVE_CALLS) { Raise_Error("Native stack overflow"); return false; } if(nparamscheck && (((nparamscheck > 0) && (nparamscheck != nargs)) || ((nparamscheck < 0) && (nargs < (-nparamscheck))))) { if (nparamscheck > 0) { Raise_Error("wrong number of parameters passed to native closure '%s' (%d passed, %d required)", sq_type(nclosure->_name) == OT_STRING ? _stringval(nclosure->_name) : "unknown", (int)nargs, (int)nparamscheck); } else { Raise_Error("wrong number of parameters passed to native closure '%s' (%d passed, at least %d required)", sq_type(nclosure->_name) == OT_STRING ? _stringval(nclosure->_name) : "unknown", (int)nargs, (int)-nparamscheck); } return false; } SQInteger tcs; SQIntVec &tc = nclosure->_typecheck; if((tcs = tc.size())) { for(SQInteger i = 0; i < nargs && i < tcs; i++) { if((tc._vals[i] != -1) && !check_typemask(sq_type(_stack._vals[newbase+i]), tc._vals[i])) { Raise_ParamTypeError(i,tc._vals[i], sq_type(_stack._vals[newbase+i]), sq_type(nclosure->_name) == OT_STRING ? _stringval(nclosure->_name) : nullptr); return false; } } } if(!EnterFrame(newbase, newtop, false)) return false; ci->_closure = nclosure; ci->_target = target; SQInteger outers = nclosure->_noutervalues; for (SQInteger i = 0; i < outers; i++) { _stack._vals[newbase+nargs+i] = nclosure->_outervalues[i]; } if(nclosure->_env) { _stack._vals[newbase] = nclosure->_env->_obj; } _nnativecalls++; SQInteger ret = (nclosure->_function)(this); _nnativecalls--; suspend = false; tailcall = false; if (ret == SQ_TAILCALL_FLAG) { tailcall = true; return true; } else if (ret == SQ_SUSPEND_FLAG) { suspend = true; } else if (ret < 0) { LeaveFrame(); Raise_Error(_lasterror); return false; } if(ret) { retval = _stack._vals[_top-1]; } else { retval.Null(); } LeaveFrame(); #if SQ_RUNTIME_TYPE_CHECK if (target != -1) { if (!check_typemask(sq_type(retval), nclosure->_result_type_mask)) { char buf[160]; sq_stringify_type_mask(buf, sizeof(buf), nclosure->_result_type_mask); Raise_Error("Function '%s' returned invalid type '%s', expected '%s'", sq_isstring(nclosure->_name) ? _stringval(nclosure->_name) : "", GetTypeName(retval), buf); return false; } } else if (nclosure->_nodiscard) { Raise_Error("Discarding return value of function '%s' with 'nodiscard' attribute", sq_isstring(nclosure->_name) ? _stringval(nclosure->_name) : ""); return false; } #endif return true; } bool SQVM::TailCall(SQClosure *closure, SQInteger parambase,SQInteger nparams) { SQInteger last_top = _top; SQObjectPtr clo(closure); if (ci->_root) { Raise_Error("root calls cannot invoke tailcalls"); return false; } for (SQInteger i = 0; i < nparams; i++) _stack._vals[_stackbase + i] = _stack._vals[_stackbase + parambase + i]; bool ret = StartCall(closure, ci->_target, nparams, _stackbase, true); if (last_top >= _top) { _top = last_top; } return ret; } bool SQVM::GetVarTrace(const SQObjectPtr &self, const SQObjectPtr &key, char * buf, int buf_size) { #if SQ_VAR_TRACE_ENABLED == 1 SQObjectPtr tmp; VarTrace * vt = NULL; const char * reason = ""; if (_ss(this)->_varTraceEnabled == false) { reason = "vartrace is disabled for this VM"; } else { switch (sq_type(self)) { case OT_TABLE: if (_table(self)->Get(key, tmp)) vt = _table(self)->GetVarTracePtr(key); else reason = "key not found"; break; case OT_ARRAY: if (sq_isnumeric(key)) { if (_array(self)->Get(tointeger(key), tmp)) vt = _array(self)->GetVarTracePtr(tointeger(key)); else reason = "index not found"; } break; default: reason = "not a table or array"; break; } } if (vt) { vt->printStack(buf, buf_size); return true; } else { *buf = 0; strncat(buf, reason, buf_size); buf[buf_size - 1] = 0; return false; } #else (void)self; (void)key; strncpy(buf, "vartrace is disabled - use build with SQ_VAR_TRACE_ENABLED=1 option set", buf_size); buf[buf_size-1] = 0; return false; #endif } bool SQVM::Get(const SQObjectPtr &self, const SQObjectPtr &key, SQObjectPtr &dest, SQUnsignedInteger getflags) { SQInteger fb = GetImpl(self, key, dest, getflags); return fb == SLOT_STATUS_OK; } SQInteger SQVM::GetImpl(const SQObjectPtr &self, const SQObjectPtr &key, SQObjectPtr &dest, SQUnsignedInteger getflags) { if (getflags & GET_FLAG_TYPE_METHODS_ONLY) { if (InvokeTypeMethod(self, key, dest)) { propagate_immutable(self, dest); return SLOT_STATUS_OK; } if ((getflags & GET_FLAG_DO_NOT_RAISE_ERROR) == 0) Raise_IdxError(key); return SLOT_STATUS_NO_MATCH; } switch(sq_type(self)){ case OT_TABLE: if(_table(self)->Get(key,dest)) { propagate_immutable(self, dest); return SLOT_STATUS_OK; } break; case OT_ARRAY: if (sq_isnumeric(key)) { if (_array(self)->Get(tointeger(key), dest)) { propagate_immutable(self, dest); return SLOT_STATUS_OK; } if ((getflags & GET_FLAG_DO_NOT_RAISE_ERROR) == 0) Raise_IdxError(key); return SLOT_STATUS_NO_MATCH; } break; case OT_INSTANCE: if(_instance(self)->Get(key,dest)) { propagate_immutable(self, dest); return SLOT_STATUS_OK; } break; case OT_CLASS: if(_class(self)->Get(key,dest)) { propagate_immutable(self, dest); return SLOT_STATUS_OK; } break; case OT_STRING: if(sq_isnumeric(key)){ SQInteger n = tointeger(key); SQInteger len = _string(self)->_len; if (n < 0) { n += len; } if (n >= 0 && n < len) { dest = SQInteger(_stringval(self)[n]); return SLOT_STATUS_OK; } if ((getflags & GET_FLAG_DO_NOT_RAISE_ERROR) == 0) Raise_IdxError(key); return SLOT_STATUS_NO_MATCH; } break; default:break; //shut up compiler } if ((getflags & GET_FLAG_RAW) == 0) { switch(FallBackGet(self,key,dest)) { case SLOT_STATUS_OK: propagate_immutable(self, dest); return SLOT_STATUS_OK; //okie case SLOT_STATUS_NO_MATCH: break; //keep falling back case SLOT_STATUS_ERROR: return SLOT_STATUS_ERROR; // the metamethod failed } } if (!(getflags & (GET_FLAG_RAW | GET_FLAG_NO_TYPE_METHODS))) { if(InvokeTypeMethod(self,key,dest)) { propagate_immutable(self, dest); return SLOT_STATUS_OK; } } if ((getflags & GET_FLAG_DO_NOT_RAISE_ERROR) == 0) Raise_IdxError(key); return SLOT_STATUS_NO_MATCH; } SQClass* SQVM::GetBuiltInClassForType(SQObjectType type) { switch(type) { case OT_INTEGER: return _class(_sharedstate->_integer_class); case OT_FLOAT: return _class(_sharedstate->_float_class); case OT_BOOL: return _class(_sharedstate->_bool_class); case OT_STRING: return _class(_sharedstate->_string_class); case OT_ARRAY: return _class(_sharedstate->_array_class); case OT_TABLE: return _class(_sharedstate->_table_class); case OT_CLOSURE: case OT_NATIVECLOSURE: return _class(_sharedstate->_function_class); case OT_GENERATOR: return _class(_sharedstate->_generator_class); case OT_THREAD: return _class(_sharedstate->_thread_class); case OT_CLASS: return _class(_sharedstate->_class_class); case OT_INSTANCE: return _class(_sharedstate->_instance_class); case OT_WEAKREF: return _class(_sharedstate->_weakref_class); case OT_USERDATA: return _class(_sharedstate->_userdata_class); case OT_NULL: return _class(_sharedstate->_null_class); default: return NULL; } } bool SQVM::InvokeTypeMethod(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest) { SQClass *builtin_class = GetBuiltInClassForType(sq_type(self)); if (!builtin_class) return false; return builtin_class->Get(key, dest); } SQInteger SQVM::FallBackGet(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest) { switch(sq_type(self)){ case OT_TABLE: case OT_USERDATA: //delegation if(_delegable(self)->_delegate) { if(Get(SQObjectPtr(_delegable(self)->_delegate),key,dest,0)) return SLOT_STATUS_OK; } else { return SLOT_STATUS_NO_MATCH; } //go through case OT_INSTANCE: { SQObjectPtr closure; if(_delegable(self)->GetMetaMethod(this, MT_GET, closure)) { Push(self);Push(key); _nmetamethodscall++; AutoDec ad(&_nmetamethodscall); if(Call(closure, 2, _top - 2, dest, SQFalse)) { Pop(2); return SLOT_STATUS_OK; } else { Pop(2); if(sq_type(_lasterror) != OT_NULL) { //NULL means "clean failure" (not found) if (_nmetamethodscall <= 1) Raise_MetamethodError("_get"); return SLOT_STATUS_ERROR; } } } } break; default: break;//shutup GCC 4.x } // no metamethod or no fallback type return SLOT_STATUS_NO_MATCH; } bool SQVM::Set(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val) { if (self._flags & SQOBJ_FLAG_IMMUTABLE) { Raise_Error("trying to modify immutable '%s'",GetTypeName(self)); return false; } switch(sq_type(self)){ case OT_TABLE: if(_table(self)->Set(key,val)) return true; break; case OT_INSTANCE: { SQInteger res = _instance(self)->Set(key,val); if (res == SLOT_STATUS_OK) return true; if (res == SLOT_STATUS_ERROR) { // This error happens only if SetNativeField() failed. // The only possible cause is type mismatch. Raise_Error("type mismatch in native field assignment"); return false; } // SLOT_STATUS_NO_MATCH, fallthrough break; } case OT_ARRAY: if(!sq_isnumeric(key)) { Raise_Error("indexing %s with %s",GetTypeName(self),GetTypeName(key)); return false; } if(!_array(self)->Set(tointeger(key),val)) { Raise_IdxError(key); return false; } return true; case OT_USERDATA: break; // must fall back default: Raise_Error("trying to set '%s'",GetTypeName(self)); return false; } switch(FallBackSet(self,key,val)) { case SLOT_STATUS_OK: return true; //okie case SLOT_STATUS_NO_MATCH: break; //keep falling back case SLOT_STATUS_ERROR: return false; // the metamethod failed } Raise_IdxError(key); return false; } SQInteger SQVM::FallBackSet(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val) { switch(sq_type(self)) { case OT_TABLE: if(_table(self)->_delegate) { if(Set(SQObjectPtr(_table(self)->_delegate),key,val)) return SLOT_STATUS_OK; } //keps on going case OT_INSTANCE: case OT_USERDATA:{ SQObjectPtr closure; SQObjectPtr t; if(_delegable(self)->GetMetaMethod(this, MT_SET, closure)) { Push(self);Push(key);Push(val); _nmetamethodscall++; AutoDec ad(&_nmetamethodscall); if(Call(closure, 3, _top - 3, t, SQFalse)) { Pop(3); return SLOT_STATUS_OK; } else { Pop(3); if(sq_type(_lasterror) != OT_NULL) { //NULL means "clean failure" (not found) if (_nmetamethodscall <= 1) Raise_MetamethodError("_set"); return SLOT_STATUS_ERROR; } } } } break; default: break;//shutup GCC 4.x } // no metamethod or no fallback type return SLOT_STATUS_NO_MATCH; } bool SQVM::Clone(const SQObjectPtr &self,SQObjectPtr &target) { SQObjectPtr temp_reg; SQObjectPtr newobj; switch(sq_type(self)){ case OT_TABLE: newobj = _table(self)->Clone(); goto cloned_mt; case OT_INSTANCE: { newobj = _instance(self)->Clone(_ss(this)); cloned_mt: SQObjectPtr closure; if(_delegable(newobj)->_delegate && _delegable(newobj)->GetMetaMethod(this,MT_CLONED,closure)) { Push(newobj); Push(self); if(!CallMetaMethod(closure,MT_CLONED,2,temp_reg)) return false; } } target = newobj; return true; case OT_ARRAY: target = _array(self)->Clone(); return true; case OT_USERDATA: case OT_CLASS: Raise_Error("cloning a %s", GetTypeName(self)); return false; default: target = self; return true; } } bool SQVM::NewSlot(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val,bool bstatic) { if (self._flags & SQOBJ_FLAG_IMMUTABLE) { Raise_Error("trying to modify immutable '%s'",GetTypeName(self)); return false; } switch(sq_type(self)) { case OT_TABLE: { bool rawcall = true; if(_table(self)->_delegate) { SQObjectPtr res; if(!_table(self)->Get(key,res)) { SQObjectPtr closure; if(_delegable(self)->_delegate && _delegable(self)->GetMetaMethod(this,MT_NEWSLOT,closure)) { Push(self);Push(key);Push(val); if(!CallMetaMethod(closure,MT_NEWSLOT,3,res)) { return false; } rawcall = false; } else { rawcall = true; } } } if(rawcall) _table(self)->NewSlot(key,val); //cannot fail break;} case OT_INSTANCE: { SQObjectPtr res; SQObjectPtr closure; if(_delegable(self)->_delegate && _delegable(self)->GetMetaMethod(this,MT_NEWSLOT,closure)) { Push(self);Push(key);Push(val); if(!CallMetaMethod(closure,MT_NEWSLOT,3,res)) { return false; } break; } Raise_Error("class instances do not support the new slot operator"); return false; break;} case OT_CLASS: if(!_class(self)->NewSlot(_ss(this),key,val,bstatic)) { if(_class(self)->isLocked()) { Raise_Error("trying to modify a class that has already been instantiated, inherited or is locked manually"); return false; } else { SQObjectPtr oval(PrintObjVal(key)); Raise_Error("the property %s already exists",_stringval(oval)); return false; } } break; default: Raise_Error("indexing %s with %s",GetTypeName(self),GetTypeName(key)); return false; break; } return true; } bool SQVM::DeleteSlot(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &res) { if (self._flags & SQOBJ_FLAG_IMMUTABLE) { Raise_Error("trying to modify immutable '%s'",GetTypeName(self)); return false; } switch(sq_type(self)) { case OT_TABLE: case OT_INSTANCE: case OT_USERDATA: { SQObjectPtr t; SQObjectPtr closure; if(_delegable(self)->_delegate && _delegable(self)->GetMetaMethod(this,MT_DELSLOT,closure)) { Push(self);Push(key); return CallMetaMethod(closure,MT_DELSLOT,2,res); } else { if(sq_type(self) == OT_TABLE) { if(_table(self)->Get(key,t)) { _table(self)->Remove(key); } else { Raise_IdxError(key); return false; } } else { Raise_Error("cannot delete a slot from %s",GetTypeName(self)); return false; } } res = t; } break; default: Raise_Error("attempt to delete a slot from a %s",GetTypeName(self)); return false; } return true; } bool SQVM::Call(const SQObjectPtr &closure,SQInteger nparams,SQInteger stackbase,SQObjectPtr &outres,SQBool invoke_err_handler) { switch(sq_type(closure)) { case OT_CLOSURE: return _debughook ? Execute(closure, nparams, stackbase, outres, invoke_err_handler) : Execute(closure, nparams, stackbase, outres, invoke_err_handler); case OT_NATIVECLOSURE:{ bool dummy; return CallNative(_nativeclosure(closure), nparams, stackbase, outres, -1, dummy, dummy); } case OT_CLASS: { SQClass *theclass = _class(closure); if (theclass->_is_builtin_type) { // For built-in types, call the constructor directly (no instance creation) SQObjectPtr constr; if (!theclass->GetConstructor(constr)) { Raise_Error("built-in type '%s' has no constructor", IdType2Name(theclass->_builtin_type_id)); return false; } assert(sq_type(constr) == OT_NATIVECLOSURE); _stack[stackbase].Null(); // 'this' bool dummy; return CallNative(_nativeclosure(constr), nparams, stackbase, outres, -1, dummy, dummy); } else { // Regular script class - create instance then call constructor SQObjectPtr constr; SQObjectPtr temp; if (!CreateClassInstance(theclass,outres,constr)) return false; SQObjectType ctype = sq_type(constr); if (ctype == OT_NATIVECLOSURE || ctype == OT_CLOSURE) { _stack[stackbase] = outres; return Call(constr,nparams,stackbase,temp,invoke_err_handler); } return true; } } default: Raise_Error("attempt to call '%s'", GetTypeName(closure)); return false; } } bool SQVM::CallMetaMethod(SQObjectPtr &closure,SQMetaMethod SQ_UNUSED_ARG(mm),SQInteger nparams,SQObjectPtr &outres) { _nmetamethodscall++; if(Call(closure, nparams, _top - nparams, outres, SQFalse)) { _nmetamethodscall--; Pop(nparams); return true; } _nmetamethodscall--; Pop(nparams); return false; } void SQVM::FindOuter(SQObjectPtr &target, SQObjectPtr *stackindex) { SQOuter **pp = &_openouters; SQOuter *p; SQOuter *otr; while ((p = *pp) != NULL && p->_valptr >= stackindex) { if (p->_valptr == stackindex) { target = SQObjectPtr(p); return; } pp = &p->_next; } otr = SQOuter::Create(_ss(this), stackindex); otr->_next = *pp; otr->_idx = (stackindex - _stack._vals); __ObjAddRef(otr); *pp = otr; target = SQObjectPtr(otr); } bool SQVM::EnterFrame(SQInteger newbase, SQInteger newtop, bool tailcall) { if( !tailcall ) { if( _callsstacksize == _alloccallsstacksize ) { GrowCallStack(); } ci = &_callsstack[_callsstacksize++]; ci->_prevstkbase = (SQInt32)(newbase - _stackbase); ci->_prevtop = (SQInt32)(_top - _stackbase); ci->_etraps = 0; ci->_ncalls = 1; ci->_generator = NULL; ci->_root = SQFalse; } else { ci->_ncalls++; } _stackbase = newbase; _top = newtop; if(newtop + STACK_GROW_THRESHOLD > (SQInteger)_stack.size()) { if(!_nmetamethodscall) { SQInteger newsize = newtop + (STACK_GROW_THRESHOLD << 1); if (newsize > MAX_SQ_STACK_SIZE) { Raise_Error("stack overflow, cannot resize stack"); return false; } _stack.resize(newsize); RelocateOuters(); } // TODO: this exception should never occur and will be removed soon else if(newtop + MIN_STACK_OVERHEAD > (SQInteger)_stack.size()) { Raise_Error("stack overflow, cannot resize stack while in a metamethod, stack.size = %d, required = %d", int(_stack.size()), int(newtop + MIN_STACK_OVERHEAD)); return false; } } return true; } void SQVM::Release() { sq_delete(_sharedstate->_alloc_ctx, this, SQVM); } void SQVM::LeaveFrame() { SQInteger last_top = _top; SQInteger last_stackbase = _stackbase; SQInteger css = --_callsstacksize; /* First clean out the call stack frame */ ci->_closure.Null(); _stackbase -= ci->_prevstkbase; _top = _stackbase + ci->_prevtop; ci = (css) ? &_callsstack[css-1] : NULL; if(_openouters) CloseOuters(&(_stack._vals[last_stackbase])); while (last_top >= _top) { _stack._vals[last_top--].Null(); } } void SQVM::RelocateOuters() { SQOuter *p = _openouters; while (p) { p->_valptr = _stack._vals + p->_idx; p = p->_next; } } void SQVM::CloseOuters(SQObjectPtr *stackindex) { SQOuter *p; while ((p = _openouters) != NULL && p->_valptr >= stackindex) { p->_value = *(p->_valptr); p->_valptr = &p->_value; _openouters = p->_next; __ObjRelease(p); } } void SQVM::Remove(SQInteger n) { n = (n >= 0)?n + _stackbase - 1:_top + n; for(SQInteger i = n; i < _top; i++){ _stack[i] = _stack[i+1]; } _stack[_top].Null(); _top--; } void SQVM::Pop() { ValidateThreadAccess(); _stack[--_top].Null(); } void SQVM::Pop(SQInteger n) { ValidateThreadAccess(); for(SQInteger i = 0; i < n; i++){ _stack[--_top].Null(); } } void SQVM::PushNull() { ValidateThreadAccess(); _stack[_top++].Null(); } void SQVM::Push(const SQObjectPtr &o) { ValidateThreadAccess(); _stack[_top++] = o; } void SQVM::Push(SQObjectPtr &&o) { ValidateThreadAccess(); _stack[_top++] = std::move(o); } SQObjectPtr &SQVM::Top() { return _stack[_top-1]; } SQObjectPtr &SQVM::PopGet() { ValidateThreadAccess(); return _stack[--_top]; } SQObjectPtr &SQVM::GetUp(SQInteger n) { ValidateThreadAccess(); return _stack[_top+n]; } SQObjectPtr &SQVM::GetAt(SQInteger n) { ValidateThreadAccess(); return _stack[n]; } #ifdef _DEBUG_DUMP void SQVM::dumpstack(SQInteger stackbase,bool dumpall) { SQInteger size=dumpall?_stack.size():_top; SQInteger n=0; printf("\n>>>>stack dump<<<<\n"); CallInfo &ci=_callsstack[_callsstacksize-1]; printf("IP: %p\n",ci._ip); printf("prev stack base: %d\n",ci._prevstkbase); printf("prev top: %d\n",ci._prevtop); for(SQInteger i=0;i");else printf(" "); printf("[" _PRINT_INT_FMT "]:",n); switch(sq_type(obj)){ case OT_FLOAT: printf("FLOAT %.3f",_float(obj));break; case OT_INTEGER: printf("INTEGER " _PRINT_INT_FMT,_integer(obj));break; case OT_BOOL: printf("BOOL %s",_integer(obj)?"true":"false");break; case OT_STRING: printf("STRING %s",_stringval(obj));break; case OT_NULL: printf("NULL"); break; case OT_TABLE: printf("TABLE %p[%p]",_table(obj),_table(obj)->_delegate);break; case OT_ARRAY: printf("ARRAY %p",_array(obj));break; case OT_CLOSURE: printf("CLOSURE [%p]",_closure(obj));break; case OT_NATIVECLOSURE: printf("NATIVECLOSURE");break; case OT_USERDATA: printf("USERDATA %p[%p]",_userdataval(obj),_userdata(obj)->_delegate);break; case OT_GENERATOR: printf("GENERATOR %p",_generator(obj));break; case OT_THREAD: printf("THREAD [%p]",_thread(obj));break; case OT_USERPOINTER: printf("USERPOINTER %p",_userpointer(obj));break; case OT_CLASS: printf("CLASS %p",_class(obj));break; case OT_INSTANCE: printf("INSTANCE %p",_instance(obj));break; case OT_WEAKREF: printf("WEAKREF %p",_weakref(obj));break; default: assert(0); break; }; printf("\n"); ++n; } } #endif ================================================ FILE: squirrel/sqvm.h ================================================ /* see copyright notice in squirrel.h */ #ifndef _SQVM_H_ #define _SQVM_H_ #include "opcodes.h" #include "sqobject.h" #define MAX_NATIVE_CALLS 100 #define MIN_STACK_OVERHEAD 15 // keep 256 stack slots reserved for locals and function arguments, // and another 256 slots for stack operations from native code #define STACK_GROW_THRESHOLD (256 * 2) #define MAX_SQ_STACK_SIZE 100000 #define SQ_SUSPEND_FLAG -666 #define SQ_TAILCALL_FLAG -777 #define GET_FLAG_RAW 0x00000001 #define GET_FLAG_DO_NOT_RAISE_ERROR 0x00000002 #define GET_FLAG_NO_TYPE_METHODS 0x00000004 #define GET_FLAG_TYPE_METHODS_ONLY 0x00000008 //base lib void sq_base_register(HSQUIRRELVM v); struct SQExceptionTrap{ SQExceptionTrap() {} SQExceptionTrap(SQInteger ss, SQInteger stackbase,SQInstruction *ip, SQInteger ex_target){ _stacksize = ss; _stackbase = stackbase; _ip = ip; _extarget = ex_target;} SQExceptionTrap(const SQExceptionTrap &et) { (*this) = et; } SQExceptionTrap &operator=(const SQExceptionTrap &et) = default; SQInteger _stackbase; SQInteger _stacksize; SQInstruction *_ip; SQInteger _extarget; }; typedef sqvector ExceptionsTraps; struct SQVM : public CHAINABLE_OBJ { struct CallInfo{ SQInstruction *_ip; SQObjectPtr *_literals; SQObjectPtr _closure; SQGenerator *_generator; SQInt32 _etraps; SQInt32 _prevstkbase; SQInt32 _prevtop; SQInt32 _target; SQInt32 _ncalls; SQBool _root; }; typedef sqvector CallInfoVec; public: void DebugHookProxy(SQInteger type, const char * sourcename, SQInteger line, const char * funcname); static void _DebugHookProxy(HSQUIRRELVM v, SQInteger type, const char * sourcename, SQInteger line, const char * funcname); enum ExecutionType { ET_CALL, ET_RESUME_GENERATOR, ET_RESUME_VM,ET_RESUME_THROW_VM }; SQVM(SQSharedState *ss); ~SQVM(); bool Init(SQVM *friendvm, SQInteger stacksize); template bool Execute(const SQObjectPtr &func, SQInteger nargs, SQInteger stackbase, SQObjectPtr &outres, SQBool invoke_err_handler, ExecutionType et = ET_CALL); //starts a native call return when the NATIVE closure returns bool CallNative(SQNativeClosure *nclosure, SQInteger nargs, SQInteger newbase, SQObjectPtr &retval, SQInt32 target, bool &suspend,bool &tailcall); bool TailCall(SQClosure *closure, SQInteger firstparam, SQInteger nparams); //starts a SQUIRREL call in the same "Execution loop" template bool StartCall(SQClosure *closure, SQInteger target, SQInteger nargs, SQInteger stackbase, bool tailcall); bool CreateClassInstance(SQClass *theclass, SQObjectPtr &inst, SQObjectPtr &constructor); //call a generic closure pure SQUIRREL or NATIVE bool Call(const SQObjectPtr &closure, SQInteger nparams, SQInteger stackbase, SQObjectPtr &outres,SQBool invoke_err_handler); SQRESULT Suspend(); bool GetVarTrace(const SQObjectPtr &self, const SQObjectPtr &key, char * buf, int buf_size); void CallDebugHook(SQInteger type,SQInteger forcedline=0); void CallErrorHandler(const SQObjectPtr &e); SQInteger GetImpl(const SQObjectPtr &self, const SQObjectPtr &key, SQObjectPtr &dest, SQUnsignedInteger getflags); bool Get(const SQObjectPtr &self, const SQObjectPtr &key, SQObjectPtr &dest, SQUnsignedInteger getflags); SQInteger FallBackGet(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest); bool InvokeTypeMethod(const SQObjectPtr &self,const SQObjectPtr &key,SQObjectPtr &dest); SQClass* GetBuiltInClassForType(SQObjectType type); bool Set(const SQObjectPtr &self, const SQObjectPtr &key, const SQObjectPtr &val); SQInteger FallBackSet(const SQObjectPtr &self,const SQObjectPtr &key,const SQObjectPtr &val); bool NewSlot(const SQObjectPtr &self, const SQObjectPtr &key, const SQObjectPtr &val,bool bstatic); bool DeleteSlot(const SQObjectPtr &self, const SQObjectPtr &key, SQObjectPtr &res); bool Clone(const SQObjectPtr &self, SQObjectPtr &target); bool ObjCmp(const SQObjectPtr &o1, const SQObjectPtr &o2,SQInteger &res); bool StringCat(const SQObjectPtr &str, const SQObjectPtr &obj, SQObjectPtr &dest); static bool IsEqual(const SQObject &o1,const SQObject &o2); static bool IsInstanceOf(const SQObject &obj, const SQClass *cls); bool ToString(const SQObjectPtr &o,SQObjectPtr &res); SQString *PrintObjVal(const SQObject &o); void Raise_Error(const char *s, ...); void Raise_Error(const SQObjectPtr &desc); void Raise_IdxError(const SQObjectPtr &o); void Raise_MetamethodError(const char *mmname); void Raise_CompareError(const SQObject &o1, const SQObject &o2); void Raise_ParamTypeError(SQInteger nparam,SQInteger typemask,SQInteger type,const char *funcname = nullptr); void FindOuter(SQObjectPtr &target, SQObjectPtr *stackindex); void RelocateOuters(); void CloseOuters(SQObjectPtr *stackindex); bool TypeOf(const SQObjectPtr &obj1, SQObjectPtr &dest); bool CallMetaMethod(SQObjectPtr &closure, SQMetaMethod mm, SQInteger nparams, SQObjectPtr &outres); bool ArithMetaMethod(SQInteger op, const SQObjectPtr &o1, const SQObjectPtr &o2, SQObjectPtr &dest); template bool Return(SQInteger _arg0, SQInteger _arg1, SQObjectPtr &retval); bool ARITH_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2); bool BW_OP(SQUnsignedInteger op,SQObjectPtr &trg,const SQObjectPtr &o1,const SQObjectPtr &o2); bool NEG_OP(SQObjectPtr &trg,const SQObjectPtr &o1); bool CMP_OP(CmpOP op, const SQObjectPtr &o1,const SQObjectPtr &o2,SQObjectPtr &res); bool CMP_OP_RES(CmpOP op, const SQObjectPtr &o1,const SQObjectPtr &o2,int &res); bool CMP_OP_RESI(CmpOP op, const SQObjectPtr &o1,const SQInteger o2,int &res); bool CMP_OP_RESF(CmpOP op, const SQObjectPtr &o1,const SQFloat o2,int &res); bool ObjCmpI(const SQObjectPtr &o1, const SQInteger o2,SQInteger &res); bool ObjCmpF(const SQObjectPtr &o1, const SQFloat o2,SQInteger &res); bool CLOSURE_OP(SQObjectPtr &target, SQFunctionProto *func); bool CLASS_OP(SQObjectPtr &target,SQInteger base); //return true if the loop is finished bool FOREACH_OP(SQObjectPtr &o1,SQObjectPtr &o2,SQObjectPtr &o3,SQObjectPtr &o4,int exitpos,int &jump); bool PLOCAL_INC(SQInteger op,SQObjectPtr &target, SQObjectPtr &a, SQObjectPtr &incr); bool DerefInc(SQInteger op,SQObjectPtr &target, SQObjectPtr &self, SQObjectPtr &key, SQObjectPtr &incr, bool postfix); #ifdef _DEBUG_DUMP void dumpstack(SQInteger stackbase=-1, bool dumpall = false); #endif #ifndef NO_GARBAGE_COLLECTOR void Mark(SQCollectable **chain); SQObjectType GetType() {return OT_THREAD;} #endif void Finalize(); void GrowCallStack() { SQInteger newsize = _alloccallsstacksize*2; _callstackdata.resize(newsize); _callsstack = &_callstackdata[0]; _alloccallsstacksize = newsize; } bool EnterFrame(SQInteger newbase, SQInteger newtop, bool tailcall); void LeaveFrame(); void Release(); //stack functions for the api void Remove(SQInteger n); static bool IsFalse(const SQObject &o); void Pop(); void Pop(SQInteger n); void Push(const SQObjectPtr &o); void Push(SQObjectPtr &&o); void PushNull(); SQObjectPtr &Top(); SQObjectPtr &PopGet(); SQObjectPtr &GetUp(SQInteger n); SQObjectPtr &GetAt(SQInteger n); #if SQ_CHECK_THREAD >= SQ_CHECK_THREAD_LEVEL_DEEP inline void ValidateThreadAccess() { assert(!_get_current_thread_id_func || check_thread_access==0 || check_thread_access==_get_current_thread_id_func()); } #else inline void ValidateThreadAccess() {} #endif inline bool CanAccessFromThisThread() { #if SQ_CHECK_THREAD >= SQ_CHECK_THREAD_LEVEL_FAST if (_nnativecalls && _get_current_thread_id_func) return _get_current_thread_id_func() == _current_thread; #endif return true; } SQObjectPtrVec _stack; SQInteger _top; SQInteger _stackbase; SQOuter *_openouters; SQObjectPtr _roottable; SQObjectPtr _lasterror; SQObjectPtr _errorhandler; bool _debughook; SQDEBUGHOOK _debughook_native; SQObjectPtr _debughook_closure; SQInteger _current_thread; SQGETTHREAD _get_current_thread_id_func; SQCOMPILELINEHOOK _compile_line_hook; SQSQCALLHOOK _sq_call_hook; SQWATCHDOGHOOK _watchdog_hook; SQObjectPtr temp_reg; CallInfo* _callsstack; SQInteger _callsstacksize; SQInteger _alloccallsstacksize; sqvector _callstackdata; ExceptionsTraps _etraps; CallInfo *ci; SQUserPointer _foreignptr; //VMs sharing the same state SQSharedState *_sharedstate; SQInteger _nnativecalls; SQInteger _nmetamethodscall; SQRELEASEHOOK _releasehook; //suspend infos SQBool _suspended; SQBool _suspended_root; SQInteger _suspended_target; SQInteger _suspended_traps; int64_t check_thread_access = 0; }; struct AutoDec{ AutoDec(SQInteger *n) { _n = n; } ~AutoDec() { (*_n)--; } SQInteger *_n; }; inline SQObjectPtr &stack_get(HSQUIRRELVM v,SQInteger idx){return ((idx>=0)?(v->GetAt(idx+v->_stackbase-1)):(v->GetUp(idx)));} #define _ss(_vm_) (_vm_)->_sharedstate #ifndef NO_GARBAGE_COLLECTOR #define _opt_ss(_vm_) (_vm_)->_sharedstate #else // This change is needed to make use of alloc_ctx // Without alloc_ctx NULL can be returned #define _opt_ss(_vm_) (_vm_)->_sharedstate #endif #endif //_SQVM_H_ ================================================ FILE: squirrel/vartrace.cpp ================================================ #include "sqpcheader.h" #include #if SQ_VAR_TRACE_ENABLED == 1 #include "squtils.h" #include "sqvm.h" #include "vartrace.h" #define VT_FLAG_STRING (1<<0) #define VT_FLAG_ELLIPSIS (1<<1) bool VarTrace::isStacksEqual(int a, int b) { for (int i = 0; i < VAR_TRACE_CALLSTACK_DEPTH; i++) { if (history[a].stack[i].line == STACK_NOT_INITIALIZED && history[b].stack[i].line == STACK_NOT_INITIALIZED) return true; if (history[a].stack[i].line != history[b].stack[i].line || history[a].stack[i].fileName != history[b].stack[i].fileName) { return false; } } return true; } void VarTrace::saveStack(const SQObject & value, const SQObject &vm_ref) { // NULL vm may happen for objects created during SQSharedState initialization assert(sq_isthread(vm_ref) || sq_isnull(vm_ref)); if (!sq_isthread(vm_ref)) return; HSQUIRRELVM vm = static_cast(vm_ref._unVal.pRefCounted); saveStack(value, vm); } void VarTrace::saveStack(const SQObject & value, HSQUIRRELVM vm) { if (!_ss(vm)->_varTraceEnabled) return; setCnt++; SQStackInfos si; SQInteger level = 0; #if VAR_TRACE_SAVE_VALUES != 0 history[pos].count = 1; const int valSize = sizeof(history[pos].val); history[pos].val[valSize - 2] = 0; SQObjectPtr obj(value); SQObjectPtr res; if (vm->ToString(obj, res)) { const char *valueAsString = sq_objtostring(&res); if (valueAsString) { strncpy(history[pos].val, valueAsString, valSize); history[pos].flags = (history[pos].val[valSize - 2] != 0 ? VT_FLAG_ELLIPSIS : 0) | (sq_isstring(value) ? VT_FLAG_STRING : 0); history[pos].val[valSize - 1] = 0; } } #endif int count = 0; while (SQ_SUCCEEDED(sq_stackinfos(vm, level, &si))) { const char *src = "unknown"; if (si.source) src = si.source; history[pos].stack[count].fileName = src; history[pos].stack[count].line = int(si.line); if (int(si.line) != -1) count++; if (count >= VAR_TRACE_CALLSTACK_DEPTH) break; level++; } if (count < VAR_TRACE_CALLSTACK_DEPTH) history[pos].stack[count].line = STACK_NOT_INITIALIZED; int prevPos = (pos - 1 + VAR_TRACE_HISTORY_SIZE) % VAR_TRACE_HISTORY_SIZE; #if VAR_TRACE_SAVE_VALUES != 0 if (memcmp(&history[pos].val, &history[prevPos].val, sizeof(history[prevPos].val)) == 0 && isStacksEqual(pos, prevPos)) { history[pos].stack[0].line = STACK_NOT_INITIALIZED; history[prevPos].count++; } else { pos++; if (pos >= VAR_TRACE_HISTORY_SIZE) pos = 0; } #else pos++; if (pos >= VAR_TRACE_HISTORY_SIZE) pos = 0; #endif } #define VT_MAX(a, b) ((a) > (b) ? (a) : (b)) #define VT_APRINTF(...) { written += scsprintf(buf + written, VT_MAX(size - written, 0), __VA_ARGS__); } void VarTrace::printStack(char * buf, int size) { size--; int written = 0; *buf = 0; for (int h = 0; h < VAR_TRACE_HISTORY_SIZE; h++) { int historyPos = (-h + pos + VAR_TRACE_HISTORY_SIZE * 2 - 1) % VAR_TRACE_HISTORY_SIZE; HistoryRecord & hist = history[historyPos]; bool stackPresent = hist.stack[0].line != STACK_NOT_INITIALIZED; if (size > written) VT_APRINTF("%d:", h); #if VAR_TRACE_SAVE_VALUES != 0 bool showCount = history[historyPos].count > 1 && stackPresent; if (showCount) VT_APRINTF(" x%d", history[historyPos].count); if (stackPresent) VT_APRINTF((history[historyPos].flags & VT_FLAG_STRING) ? " value='%s%s'" : " value=%s%s", history[historyPos].val, (history[historyPos].flags & VT_FLAG_ELLIPSIS) ? "..." : ""); VT_APRINTF("\n"); #endif VT_APRINTF("\n"); for (int i = 0; i < VAR_TRACE_CALLSTACK_DEPTH; i++) { if (hist.stack[i].line == STACK_NOT_INITIALIZED) break; if (size > written) VT_APRINTF(" %s:%d\n", hist.stack[i].fileName, hist.stack[i].line); } VT_APRINTF("\n"); } VT_APRINTF("set counter = %d\n", setCnt); } #endif ================================================ FILE: squirrel/vartrace.h ================================================ #pragma once #include // memset #include #include "squtils.h" #define VAR_TRACE_SAVE_VALUES 0 #define VAR_TRACE_CALLSTACK_DEPTH 4 #define VAR_TRACE_HISTORY_SIZE 4 #define STACK_NOT_INITIALIZED (-2) struct VarTrace { struct VarStackRecord { const char * fileName; int line; }; struct HistoryRecord { VarStackRecord stack[VAR_TRACE_CALLSTACK_DEPTH]; #if VAR_TRACE_SAVE_VALUES != 0 int count; char val[31]; char flags; #endif }; int pos; int setCnt; HistoryRecord history[VAR_TRACE_HISTORY_SIZE]; VarTrace() { memset(this, 0, sizeof(*this)); clear(); } void clear() { pos = 0; setCnt = 0; #if VAR_TRACE_SAVE_VALUES != 0 history[0].count = 0; #endif for (int i = 0; i < VAR_TRACE_HISTORY_SIZE; i++) { history[i].stack[0].line = STACK_NOT_INITIALIZED; #if VAR_TRACE_SAVE_VALUES != 0 history[i].val[0] = 0; #endif } } void saveStack(const SQObject & value, const SQObject &vm); void saveStack(const SQObject & value, HSQUIRRELVM vm); void printStack(char * buf, int size); private: bool isStacksEqual(int a, int b); }; #if SQ_VAR_TRACE_ENABLED == 1 #define VT_CODE(code) code #define VT_COMMA , #define VT_DECL_VEC SQVarTraceVec varTrace #define VT_DECL_CTR(ss_) , varTrace(ss_) #define VT_DECL_SINGLE VarTrace varTrace #define VT_DECL_ARG VT_COMMA VarTrace * var_trace_arg #define VT_DECL_ARG_DEF VT_DECL_ARG = NULL #define VT_ARG VT_COMMA var_trace #define VT_ARG_PARAM var_trace #define VT_REF(ptr) VT_COMMA &(ptr->varTrace) #define VT_RESIZE(x) varTrace.resize(x) #define VT_RESERVE(x) varTrace.reserve(x) #define VT_TRACE(x, val, vm) varTrace[x].saveStack(val, vm) #define VT_TRACE_SINGLE(ptr, val, vm) ptr->varTrace.saveStack(val, vm) #define VT_CLEAR_SINGLE(ptr) ptr->varTrace.clear() #define VT_INSERT(x, val, vm) { VarTrace tmp; varTrace.insert(x, tmp); VT_TRACE(x, val, vm); } #define VT_PUSHBACK(val, vm) { VarTrace tmp; varTrace.push_back(tmp); VT_TRACE(varTrace.size() - 1, val, vm); } #define VT_POPBACK() varTrace.pop_back() #define VT_REMOVE(x) varTrace.remove(x) #define VT_CLONE_FROM_TO(from, to) to->varTrace.copy(from->varTrace) #define VT_COPY_SINGLE(from, to) to->varTrace = from->varTrace typedef sqvector SQVarTraceVec; #else #define VT_CODE(code) #define VT_COMMA #define VT_DECL_VEC #define VT_DECL_CTR(ss_) #define VT_DECL_SINGLE #define VT_DECL_ARG #define VT_DECL_ARG_DEF #define VT_ARG VT_COMMA #define VT_ARG_PARAM #define VT_REF(ptr) #define VT_RESIZE(x) #define VT_RESERVE(x) #define VT_TRACE(x, val, vm) #define VT_TRACE_SINGLE(ptr, val, vm) #define VT_CLEAR_SINGLE(ptr) #define VT_INSERT(x, val, vm) #define VT_PUSHBACK(val, vm) #define VT_POPBACK() #define VT_REMOVE(x) #define VT_CLONE_FROM_TO(from, to) #define VT_COPY_SINGLE(from, to) #endif ================================================ FILE: squirrel/vartracestub.cpp ================================================ #include "vartrace.h" void VarTrace::printStack(char *, int) {} ================================================ FILE: squirrel-config.cmake.in ================================================ @PACKAGE_INIT@ include("${CMAKE_CURRENT_LIST_DIR}/squirrel-config-version.cmake") include("${CMAKE_CURRENT_LIST_DIR}/squirrel-targets.cmake") ================================================ FILE: testData/ast/ast_render/all_syntax.nut ================================================ from "math" import sin, cos as cosine from "string" import * import "datetime" import "debug" as Debug let { getlocals, doc } = require("debug") { null; null; null; } if (__FILE__ && __LINE__) null; println(); local tab = {} enum Enum1 {} enum Enum2 { AA } enum Enum3 { AA, } enum Enum4 { AA BB } enum Enum5 { AA, BB } enum Enum6 { AA, BB, } enum Enum7 { AA = -101, BB, } enum Enum8 { AA = 102, BB = "enum_str1", } enum Enum9 { AA BB = "enum_str2", } global enum Enum10 {CC = null} #allow-auto-freeze const cn0 = 201 const cn1 = "const_str1" const cn2 = ["const_str2", "const_str3"] const cn3 = {abc = "const_str4"} global const cn4 = true local a0; local m0, m1; local a1 = null local a2 = 123_456 local a3 = 123.25 local a4 = 3.5e12 local a5 = 3.5e+12 local a6 = 3.5e-12 local a7 = -3.5e-12 local a8 = true local a9 = false local a10 = "\tlocal_str1\uFDF0" local a11 = @"local_str2 local_str3" local a12 = $"a9={a9}" local a13 = (123_458).tofloat() local a14 = (12.45).tostring() local m3 = 301, m4 = 302 local a15 = Enum9.AA local a16 = m4 = 303 let b0 = 0x401 let b1 = 0x402_FF_AA let b2 = 'B' let b3 = '\n' let b4 = '\t'.tochar() let e0 = [] let e1 = [501] let e2 = [502,] let e3 = [503,504] let e4 = [505,506,] let e5 = [507, 508, ] let e6 = [509 510] let e7 = [511 512] let t0 = {} let t1 = {x = 601} let t2 = {"x": 602} let t3 = {["x"] = 603} let t4 = {[null] = 604} let t5 = {[({y = 605})] = 606} let t6 = {a2} let t7 = {a3 a8} let t8 = {x = 1}.__merge({y = 607}) let t9 = clone {z = 608} tab.x <- "new_slot_str1" tab["y"] <- "new_slot_str2" local { abc } = cn3 { local { y, } = t8 } { local { y = 609, x } = t2 println(x) } { local { y: int = 609, x: string|int|null } = t2 println(x) } local [d1, d2] = e3 local [d11: int, d12: (int | null)] = e3 local [d21, d22: (int | null)] = e3 local [d31: int, d32] = e3 function type_test1(x: int): int { return 1 + x } function type_test2(x: int, ...: float): int { return 1 + x } function type_test3(x: number|null): null|number { return 1.0 + (x ?? 5) } function type_test4(x: number|null = null): null|number { return 1.0 + (x ?? 6) } function type_test5(x: (number | null)): (null | number) { return 1.0 + (x ?? 5) } function type_test6(x: (number|null) = null): (int|float) { return 1.0 + (x ?? 6) } function type_test7(x: bool|number|int|float|string|table|array|userdata|function|generator|userpointer|thread|instance|class|weakref|null, y: any): any { return x + y } let type_test8 = @(x: bool|number|int|float|string|table|array|userdata|function|generator|userpointer|thread|instance|class|weakref|null, y: any): any ( x + y ) let type_test9 = @type_test9(x: bool|number|int|null, y: any): any ( x + y ) let type_test10 = (@ [pure,] type_test10(x: bool|number|int|null, y: any): any ( x + y )) let x901: int = 5 local t902: table = {} local t903: table|array = {} class C4 { static x = true y = false constructor() {} function fn1() {} fn2 = function() { this.fn1() } fn3 = function fn3() { this.fn1() } fn4 = @() "class_str1" fn5 = @fn5() "class_str2" _call = function (name) { this[name](); this.constructor(); ::tab.y <- 610 } } let instance = C4() instance.fn4() instance?.fn4() instance?["fn4"]() instance.fn4?() if (instance instanceof C4) { println("instance instanceof C4") } function gfn1() {} local gfn2 = function() { this.gfn1() } let gfn3 = function gfn3() { this.gfn1() } local gfn4 = @() "lambda_str1" let gfn5 = @gfn5() "lambda_str2" local _call = function (name) { this[name]() } let gfn_pure = @[pure] gfn5() "lambda_str3" let gfn_nodiscard = @[nodiscard] gfn6() "lambda_str4" let gfn_pure_nodiscard = @[pure, nodiscard] gfn7() "lambda_str5" function fa1(x) { return x } function fa2(x, y) { return x + y; } function fa3(x, y, z) { return x + y * z; } function fa4(x, ...) { return x + vargv.len(); } function fa5(x = [1, 2]) { return x?[1]; } function fa6(x, y = null) { return y; } function fa7(x, y = {a = 4}, ) { return y; } function fa8(x, y = {a = 5}) { return y; } function [pure,] pure_fn(x) {return x * x} function [nodiscard,pure,] pure_nodiscard_fn(x) {return x * x} function coroutine_fn() { yield 1100; yield 1200; } println(resume coroutine_fn()) function des1({x}) { return x + 1 } function des2([x]) { return x + 1 } function des3([x], {y}) { return x + y } function des4([x, z, w,], {y}) { return x + y } function des5(x, {y = null}) { return x + (y ?? 2) } function des6(x, {y = null, z: int|float = 1000}) { return x + (y ?? 2) + z } function [nodiscard, pure] des7([x: function = @() @() @() null, t, r: string = "abc555", g = "abc666"], {y = null, z: int|float = 1000,}, ...) { let {p, i} = t println() return x + (y ?? 2) + z; } let i0 = 1 + 2 let i1 = 1 - 2 let i2 = 1 * 2 let i3 = 1 / 2 let i4 = 1 % 2 let i5 = 1 ^ 2 let i6 = 1 & 2 let i7 = 1 | 2 let i8 = 1 || 2 let i9 = 1 && 2 let i10 = 1 == 2 let i11 = 1 != 2 let i12 = 1 < 2 let i13 = 1 > 2 let i14 = 1 <= 2 let i15 = 1 >= 2 let i16 = 1 <=> 2 let i17 = -(123) let i18 = ~(123) let i19 = !(123) let i20 = typeof 123 let i21 = const 123 let i22 = static 123 let i23 = "x" in {x = 5} let i24 = "x" not in {x = 6} let i25 = 1 >> 2 let i26 = 1 << 2 let i27 = 1 >>> 2 let i28 = 1 ?? 2 local iv = 123 iv++ iv-- --iv ++iv iv += 123 iv -= 123 iv *= 123 iv /= 123 iv %= 123 //iv |= 123 //iv ^= 123 //iv &= 123 //iv ||= 123 //iv ^^= 123 //iv &&= 123 /* comment */ if (base) { println("base != null") } { ;;;;;; } if (701 == 701) fa1(701) if (702 == 703) fa1(702) else fa2(703, 704) if (705 == 706) fa1(707) else if (708 == 709) fa2(710, 711) else fa2(712, 713) if (local cv = iv) println(cv) if (local cv: int|null = iv) println(cv) else println(cv - 1) if (let cv: int = iv; cv > -1000000) println(cv) if (local cv = iv) println(cv) else if (local cv2 = iv) println("fail") while (iv--) fa1(714) do fa1(715) while (iv++ < 3) foreach (i in e1) fa1(i) foreach (k, v in t7) fa2(k, v) for (;;) break for (local iter = 0; iter < 4; ++iter) continue; for (local iter = 0; iter < 4; iter += 2, iter--) continue; try fa1(716) catch (e) fa1(717) try throw null catch (e) fa1(718) #forbid-auto-freeze local w = getlocals() local res = [] foreach (k, v in w) res.append($"{k} = {["integer", "bool", "string", "float"].indexof(type(v)) >= 0 ? v : type(v)}") res.sort() foreach (_, v in res) println(v) class A { @@"Class docstring" function x() { return 0; } } let instance_a = A() function test() { return { @@"Table docstring" x = 4 fn = function() { @@"Function docstring" return 1234 } } } println(doc(A)) println(doc(instance_a)) println(doc(test())) println(doc(test().fn)) println("done") ================================================ FILE: testData/ast/ast_render/all_syntax.opt.txt ================================================ Block isRoot = 1, isBody = 0 |-ImportStmt module 'math' | |-'sin' | `-'cos' as 'cosine' |-ImportStmt module 'string' | '*' |-ImportStmt module 'datetime' |-ImportStmt module 'debug' as 'Debug' |-DestructuringDecl type = 'table' | |-Initializer | | CallExpr | | |-Callee | | | Id 'require' | | `-Arguments | | Argument #1 | | LiteralExpr LK_STRING "debug" | |-VarDecl 'let', name = 'getlocals', type = 'any' | `-VarDecl 'let', name = 'doc', type = 'any' |-Block isRoot = 0, isBody = 0 | |-ExprStatement | | LiteralExpr LK_NULL | |-ExprStatement | | LiteralExpr LK_NULL | `-ExprStatement | LiteralExpr LK_NULL |-IfStatement | |-Condition | | BinExpr '&&' | | |-Left | | | LiteralExpr LK_STRING "testData/ast/ast_render/all_syntax.nut" | | `-Right | | LiteralExpr LK_INT 14 | `-ThenBranch | Block isRoot = 0, isBody = 0 | ExprStatement | LiteralExpr LK_NULL |-ExprStatement | CallExpr | Callee | Id 'println' |-VarDecl 'local', name = 'tab', type = 'any' | Initializer | TableExpr |-EnumDecl name = 'Enum1', isGlobal = 0 |-EnumDecl name = 'Enum2', isGlobal = 0 | Member 'AA' | LiteralExpr LK_INT 0 |-EnumDecl name = 'Enum3', isGlobal = 0 | Member 'AA' | LiteralExpr LK_INT 0 |-EnumDecl name = 'Enum4', isGlobal = 0 | |-Member 'AA' | | LiteralExpr LK_INT 0 | `-Member 'BB' | LiteralExpr LK_INT 1 |-EnumDecl name = 'Enum5', isGlobal = 0 | |-Member 'AA' | | LiteralExpr LK_INT 0 | `-Member 'BB' | LiteralExpr LK_INT 1 |-EnumDecl name = 'Enum6', isGlobal = 0 | |-Member 'AA' | | LiteralExpr LK_INT 0 | `-Member 'BB' | LiteralExpr LK_INT 1 |-EnumDecl name = 'Enum7', isGlobal = 0 | |-Member 'AA' | | LiteralExpr LK_INT -101 | `-Member 'BB' | LiteralExpr LK_INT 0 |-EnumDecl name = 'Enum8', isGlobal = 0 | |-Member 'AA' | | LiteralExpr LK_INT 102 | `-Member 'BB' | LiteralExpr LK_STRING "enum_str1" |-EnumDecl name = 'Enum9', isGlobal = 0 | |-Member 'AA' | | LiteralExpr LK_INT 0 | `-Member 'BB' | LiteralExpr LK_STRING "enum_str2" |-EnumDecl name = 'Enum10', isGlobal = 1 | Member 'CC' | LiteralExpr LK_NULL |-DirectiveStmt |-ConstDecl name = 'cn0', isGlobal = 0 | LiteralExpr LK_INT 201 |-ConstDecl name = 'cn1', isGlobal = 0 | LiteralExpr LK_STRING "const_str1" |-ConstDecl name = 'cn2', isGlobal = 0 | ArrayExpr | |-Element #1 | | LiteralExpr LK_STRING "const_str2" | `-Element #2 | LiteralExpr LK_STRING "const_str3" |-ConstDecl name = 'cn3', isGlobal = 0 | TableExpr | Field | |-Key | | LiteralExpr LK_STRING "abc" | `-Value | LiteralExpr LK_STRING "const_str4" |-ConstDecl name = 'cn4', isGlobal = 1 | LiteralExpr LK_BOOL true |-VarDecl 'local', name = 'a0', type = 'any' |-DeclGroup | |-VarDecl 'local', name = 'm0', type = 'any' | `-VarDecl 'local', name = 'm1', type = 'any' |-VarDecl 'local', name = 'a1', type = 'any' | Initializer | LiteralExpr LK_NULL |-VarDecl 'local', name = 'a2', type = 'any' | Initializer | LiteralExpr LK_INT 123456 |-VarDecl 'local', name = 'a3', type = 'any' | Initializer | LiteralExpr LK_FLOAT 123.250000 |-VarDecl 'local', name = 'a4', type = 'any' | Initializer | LiteralExpr LK_FLOAT 3499999887360.000000 |-VarDecl 'local', name = 'a5', type = 'any' | Initializer | LiteralExpr LK_FLOAT 3499999887360.000000 |-VarDecl 'local', name = 'a6', type = 'any' | Initializer | LiteralExpr LK_FLOAT 0.000000 |-VarDecl 'local', name = 'a7', type = 'any' | Initializer | LiteralExpr LK_FLOAT -0.000000 |-VarDecl 'local', name = 'a8', type = 'any' | Initializer | LiteralExpr LK_BOOL true |-VarDecl 'local', name = 'a9', type = 'any' | Initializer | LiteralExpr LK_BOOL false |-VarDecl 'local', name = 'a10', type = 'any' | Initializer | LiteralExpr LK_STRING " local_str1ﷰ" |-VarDecl 'local', name = 'a11', type = 'any' | Initializer | LiteralExpr LK_STRING "local_str2 local_str3" |-VarDecl 'local', name = 'a12', type = 'any' | Initializer | CallExpr | |-Callee | | GetFieldExpr '.' fieldName = 'subst' | | Receiver | | LiteralExpr LK_STRING "a9={0}" | `-Arguments | Argument #1 | Id 'a9' |-VarDecl 'local', name = 'a13', type = 'any' | Initializer | CallExpr | Callee | GetFieldExpr '.' fieldName = 'tofloat' | Receiver | UnExpr '(' | LiteralExpr LK_INT 123458 |-VarDecl 'local', name = 'a14', type = 'any' | Initializer | CallExpr | Callee | GetFieldExpr '.' fieldName = 'tostring' | Receiver | UnExpr '(' | LiteralExpr LK_FLOAT 12.450000 |-DeclGroup | |-VarDecl 'local', name = 'm3', type = 'any' | | Initializer | | LiteralExpr LK_INT 301 | `-VarDecl 'local', name = 'm4', type = 'any' | Initializer | LiteralExpr LK_INT 302 |-VarDecl 'local', name = 'a15', type = 'any' | Initializer | GetFieldExpr '.' fieldName = 'AA' | Receiver | Id 'Enum9' |-VarDecl 'local', name = 'a16', type = 'any' | Initializer | BinExpr '=' | |-Left | | Id 'm4' | `-Right | LiteralExpr LK_INT 303 |-VarDecl 'let', name = 'b0', type = 'any' | Initializer | LiteralExpr LK_INT 1025 |-VarDecl 'let', name = 'b1', type = 'any' | Initializer | LiteralExpr LK_INT 67305386 |-VarDecl 'let', name = 'b2', type = 'any' | Initializer | LiteralExpr LK_INT 66 |-VarDecl 'let', name = 'b3', type = 'any' | Initializer | LiteralExpr LK_INT 10 |-VarDecl 'let', name = 'b4', type = 'any' | Initializer | CallExpr | Callee | GetFieldExpr '.' fieldName = 'tochar' | Receiver | LiteralExpr LK_INT 9 |-VarDecl 'let', name = 'e0', type = 'any' | Initializer | ArrayExpr |-VarDecl 'let', name = 'e1', type = 'any' | Initializer | ArrayExpr | Element #1 | LiteralExpr LK_INT 501 |-VarDecl 'let', name = 'e2', type = 'any' | Initializer | ArrayExpr | Element #1 | LiteralExpr LK_INT 502 |-VarDecl 'let', name = 'e3', type = 'any' | Initializer | ArrayExpr | |-Element #1 | | LiteralExpr LK_INT 503 | `-Element #2 | LiteralExpr LK_INT 504 |-VarDecl 'let', name = 'e4', type = 'any' | Initializer | ArrayExpr | |-Element #1 | | LiteralExpr LK_INT 505 | `-Element #2 | LiteralExpr LK_INT 506 |-VarDecl 'let', name = 'e5', type = 'any' | Initializer | ArrayExpr | |-Element #1 | | LiteralExpr LK_INT 507 | `-Element #2 | LiteralExpr LK_INT 508 |-VarDecl 'let', name = 'e6', type = 'any' | Initializer | ArrayExpr | |-Element #1 | | LiteralExpr LK_INT 509 | `-Element #2 | LiteralExpr LK_INT 510 |-VarDecl 'let', name = 'e7', type = 'any' | Initializer | ArrayExpr | |-Element #1 | | LiteralExpr LK_INT 511 | `-Element #2 | LiteralExpr LK_INT 512 |-VarDecl 'let', name = 't0', type = 'any' | Initializer | TableExpr |-VarDecl 'let', name = 't1', type = 'any' | Initializer | TableExpr | Field | |-Key | | LiteralExpr LK_STRING "x" | `-Value | LiteralExpr LK_INT 601 |-VarDecl 'let', name = 't2', type = 'any' | Initializer | TableExpr | Field | |-Key | | LiteralExpr LK_STRING "x" | `-Value | LiteralExpr LK_INT 602 |-VarDecl 'let', name = 't3', type = 'any' | Initializer | TableExpr | Field | |-Key | | LiteralExpr LK_STRING "x" | `-Value | LiteralExpr LK_INT 603 |-VarDecl 'let', name = 't4', type = 'any' | Initializer | TableExpr | Field | |-Key | | LiteralExpr LK_NULL | `-Value | LiteralExpr LK_INT 604 |-VarDecl 'let', name = 't5', type = 'any' | Initializer | TableExpr | Field | |-Key | | UnExpr '(' | | TableExpr | | Field | | |-Key | | | LiteralExpr LK_STRING "y" | | `-Value | | LiteralExpr LK_INT 605 | `-Value | LiteralExpr LK_INT 606 |-VarDecl 'let', name = 't6', type = 'any' | Initializer | TableExpr | Field | |-Key | | LiteralExpr LK_STRING "a2" | `-Value | Id 'a2' |-VarDecl 'let', name = 't7', type = 'any' | Initializer | TableExpr | |-Field | | |-Key | | | LiteralExpr LK_STRING "a3" | | `-Value | | Id 'a3' | `-Field | |-Key | | LiteralExpr LK_STRING "a8" | `-Value | Id 'a8' |-VarDecl 'let', name = 't8', type = 'any' | Initializer | CallExpr | |-Callee | | GetFieldExpr '.' fieldName = '__merge' | | Receiver | | TableExpr | | Field | | |-Key | | | LiteralExpr LK_STRING "x" | | `-Value | | LiteralExpr LK_INT 1 | `-Arguments | Argument #1 | TableExpr | Field | |-Key | | LiteralExpr LK_STRING "y" | `-Value | LiteralExpr LK_INT 607 |-VarDecl 'let', name = 't9', type = 'any' | Initializer | UnExpr 'CLONE' | TableExpr | Field | |-Key | | LiteralExpr LK_STRING "z" | `-Value | LiteralExpr LK_INT 608 |-ExprStatement | BinExpr '<-' | |-Left | | GetFieldExpr '.' fieldName = 'x' | | Receiver | | Id 'tab' | `-Right | LiteralExpr LK_STRING "new_slot_str1" |-ExprStatement | BinExpr '<-' | |-Left | | GetSlotExpr '[' | | |-Receiver | | | Id 'tab' | | `-Key | | LiteralExpr LK_STRING "y" | `-Right | LiteralExpr LK_STRING "new_slot_str2" |-DestructuringDecl type = 'table' | |-Initializer | | Id 'cn3' | `-VarDecl 'local', name = 'abc', type = 'any' |-Block isRoot = 0, isBody = 0 | DestructuringDecl type = 'table' | |-Initializer | | Id 't8' | `-VarDecl 'local', name = 'y', type = 'any' |-Block isRoot = 0, isBody = 0 | |-DestructuringDecl type = 'table' | | |-Initializer | | | Id 't2' | | |-VarDecl 'local', name = 'y', type = 'any' | | | Initializer | | | LiteralExpr LK_INT 609 | | `-VarDecl 'local', name = 'x', type = 'any' | `-ExprStatement | CallExpr | |-Callee | | Id 'println' | `-Arguments | Argument #1 | Id 'x' |-Block isRoot = 0, isBody = 0 | |-DestructuringDecl type = 'table' | | |-Initializer | | | Id 't2' | | |-VarDecl 'local', name = 'y', type = 'int' | | | Initializer | | | LiteralExpr LK_INT 609 | | `-VarDecl 'local', name = 'x', type = 'int|string|null' | `-ExprStatement | CallExpr | |-Callee | | Id 'println' | `-Arguments | Argument #1 | Id 'x' |-DestructuringDecl type = 'array' | |-Initializer | | Id 'e3' | |-VarDecl 'local', name = 'd1', type = 'any' | `-VarDecl 'local', name = 'd2', type = 'any' |-DestructuringDecl type = 'array' | |-Initializer | | Id 'e3' | |-VarDecl 'local', name = 'd11', type = 'int' | `-VarDecl 'local', name = 'd12', type = 'int|null' |-DestructuringDecl type = 'array' | |-Initializer | | Id 'e3' | |-VarDecl 'local', name = 'd21', type = 'any' | `-VarDecl 'local', name = 'd22', type = 'int|null' |-DestructuringDecl type = 'array' | |-Initializer | | Id 'e3' | |-VarDecl 'local', name = 'd31', type = 'int' | `-VarDecl 'local', name = 'd32', type = 'any' |-VarDecl 'let', name = 'type_test1', type = 'any' | Initializer | FunctionExpr name = 'type_test1', pure = 0, nodiscard = 0, resultType = 'int' | |-Parameters count = 1 | | Parameter #1 name = 'x', type = 'int' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '+' | |-Left | | LiteralExpr LK_INT 1 | `-Right | Id 'x' |-VarDecl 'let', name = 'type_test2', type = 'any' | Initializer | FunctionExpr name = 'type_test2', pure = 0, nodiscard = 0, resultType = 'int' | |-Parameters count = 2 | | |-Parameter #1 name = 'x', type = 'int' | | |-Parameter #2 name = 'vargv', type = 'float' (vararg) | | `-Vararg... | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '+' | |-Left | | LiteralExpr LK_INT 1 | `-Right | Id 'x' |-VarDecl 'let', name = 'type_test3', type = 'any' | Initializer | FunctionExpr name = 'type_test3', pure = 0, nodiscard = 0, resultType = 'number|null' | |-Parameters count = 1 | | Parameter #1 name = 'x', type = 'number|null' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '+' | |-Left | | LiteralExpr LK_FLOAT 1.000000 | `-Right | UnExpr '(' | BinExpr '??' | |-Left | | Id 'x' | `-Right | LiteralExpr LK_INT 5 |-VarDecl 'let', name = 'type_test4', type = 'any' | Initializer | FunctionExpr name = 'type_test4', pure = 0, nodiscard = 0, resultType = 'number|null' | |-Parameters count = 1 | | Parameter #1 name = 'x', type = 'number|null' (has default) | | DefaultValue | | LiteralExpr LK_NULL | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '+' | |-Left | | LiteralExpr LK_FLOAT 1.000000 | `-Right | UnExpr '(' | BinExpr '??' | |-Left | | Id 'x' | `-Right | LiteralExpr LK_INT 6 |-VarDecl 'let', name = 'type_test5', type = 'any' | Initializer | FunctionExpr name = 'type_test5', pure = 0, nodiscard = 0, resultType = 'number|null' | |-Parameters count = 1 | | Parameter #1 name = 'x', type = 'number|null' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '+' | |-Left | | LiteralExpr LK_FLOAT 1.000000 | `-Right | UnExpr '(' | BinExpr '??' | |-Left | | Id 'x' | `-Right | LiteralExpr LK_INT 5 |-VarDecl 'let', name = 'type_test6', type = 'any' | Initializer | FunctionExpr name = 'type_test6', pure = 0, nodiscard = 0, resultType = 'number' | |-Parameters count = 1 | | Parameter #1 name = 'x', type = 'number|null' (has default) | | DefaultValue | | LiteralExpr LK_NULL | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '+' | |-Left | | LiteralExpr LK_FLOAT 1.000000 | `-Right | UnExpr '(' | BinExpr '??' | |-Left | | Id 'x' | `-Right | LiteralExpr LK_INT 6 |-VarDecl 'let', name = 'type_test7', type = 'any' | Initializer | FunctionExpr name = 'type_test7', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 2 | | |-Parameter #1 name = 'x', type = 'bool|number|string|table|array|userdata|function|generator|userpointer|thread|instance|class|weakref|null' | | `-Parameter #2 name = 'y', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '+' | |-Left | | Id 'x' | `-Right | Id 'y' |-VarDecl 'let', name = 'type_test8', type = 'any' | Initializer | FunctionExpr 'lambda' name = 'type_test8', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 2 | | |-Parameter #1 name = 'x', type = 'bool|number|string|table|array|userdata|function|generator|userpointer|thread|instance|class|weakref|null' | | `-Parameter #2 name = 'y', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | UnExpr '(' | BinExpr '+' | |-Left | | Id 'x' | `-Right | Id 'y' |-VarDecl 'let', name = 'type_test9', type = 'any' | Initializer | FunctionExpr 'lambda' name = 'type_test9', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 2 | | |-Parameter #1 name = 'x', type = 'bool|number|null' | | `-Parameter #2 name = 'y', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | UnExpr '(' | BinExpr '+' | |-Left | | Id 'x' | `-Right | Id 'y' |-VarDecl 'let', name = 'type_test10', type = 'any' | Initializer | UnExpr '(' | FunctionExpr 'lambda' name = 'type_test10', pure = 1, nodiscard = 0, resultType = 'any' | |-Parameters count = 2 | | |-Parameter #1 name = 'x', type = 'bool|number|null' | | `-Parameter #2 name = 'y', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | UnExpr '(' | BinExpr '+' | |-Left | | Id 'x' | `-Right | Id 'y' |-VarDecl 'let', name = 'x901', type = 'int' | Initializer | LiteralExpr LK_INT 5 |-VarDecl 'local', name = 't902', type = 'table' | Initializer | TableExpr |-VarDecl 'local', name = 't903', type = 'table|array' | Initializer | TableExpr |-VarDecl 'let', name = 'C4', type = 'any' | Initializer | ClassExpr | |-Key: | `-Members | |-LiteralExpr LK_BOOL true | |-LiteralExpr LK_BOOL false | |-FunctionExpr name = 'constructor', pure = 0, nodiscard = 0, resultType = 'any' | | |-Parameters count = 0 | | `-Body | | Block isRoot = 0, isBody = 1 | |-FunctionExpr name = 'fn1', pure = 0, nodiscard = 0, resultType = 'any' | | |-Parameters count = 0 | | `-Body | | Block isRoot = 0, isBody = 1 | |-FunctionExpr name = '(all_syntax.nut:174)', pure = 0, nodiscard = 0, resultType = 'any' | | |-Parameters count = 0 | | `-Body | | Block isRoot = 0, isBody = 1 | | ExprStatement | | CallExpr | | Callee | | GetFieldExpr '.' fieldName = 'fn1' | | Receiver | | Id 'this' | |-FunctionExpr name = 'fn3', pure = 0, nodiscard = 0, resultType = 'any' | | |-Parameters count = 0 | | `-Body | | Block isRoot = 0, isBody = 1 | | ExprStatement | | CallExpr | | Callee | | GetFieldExpr '.' fieldName = 'fn1' | | Receiver | | Id 'this' | |-FunctionExpr 'lambda' name = '(all_syntax.nut:176)', pure = 0, nodiscard = 0, resultType = 'any' | | |-Parameters count = 0 | | `-Body | | Block isRoot = 0, isBody = 1 | | ReturnStatement | | LiteralExpr LK_STRING "class_str1" | |-FunctionExpr 'lambda' name = 'fn5', pure = 0, nodiscard = 0, resultType = 'any' | | |-Parameters count = 0 | | `-Body | | Block isRoot = 0, isBody = 1 | | ReturnStatement | | LiteralExpr LK_STRING "class_str2" | `-FunctionExpr name = '(all_syntax.nut:178)', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 1 | | Parameter #1 name = 'name', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | |-ExprStatement | | CallExpr | | Callee | | GetSlotExpr '[' | | |-Receiver | | | Id 'this' | | `-Key | | Id 'name' | |-ExprStatement | | CallExpr | | Callee | | GetFieldExpr '.' fieldName = 'constructor' | | Receiver | | Id 'this' | `-ExprStatement | BinExpr '<-' | |-Left | | GetFieldExpr '.' fieldName = 'y' | | Receiver | | GetFieldExpr '.' fieldName = 'tab' | | Receiver | | RootTableAccessExpr | `-Right | LiteralExpr LK_INT 610 |-VarDecl 'let', name = 'instance', type = 'any' | Initializer | CallExpr | Callee | Id 'C4' |-ExprStatement | CallExpr | Callee | GetFieldExpr '.' fieldName = 'fn4' | Receiver | Id 'instance' |-ExprStatement | CallExpr | Callee | GetFieldExpr '?.' fieldName = 'fn4' | Receiver | Id 'instance' |-ExprStatement | CallExpr | Callee | GetSlotExpr '?[' | |-Receiver | | Id 'instance' | `-Key | LiteralExpr LK_STRING "fn4" |-ExprStatement | CallExpr | Callee | GetFieldExpr '.' fieldName = 'fn4' | Receiver | Id 'instance' |-IfStatement | |-Condition | | BinExpr 'INSTANCEOF' | | |-Left | | | Id 'instance' | | `-Right | | Id 'C4' | `-ThenBranch | Block isRoot = 0, isBody = 0 | ExprStatement | CallExpr | |-Callee | | Id 'println' | `-Arguments | Argument #1 | LiteralExpr LK_STRING "instance instanceof C4" |-VarDecl 'let', name = 'gfn1', type = 'any' | Initializer | FunctionExpr name = 'gfn1', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 |-VarDecl 'local', name = 'gfn2', type = 'any' | Initializer | FunctionExpr name = '(all_syntax.nut:196)', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ExprStatement | CallExpr | Callee | GetFieldExpr '.' fieldName = 'gfn1' | Receiver | Id 'this' |-VarDecl 'let', name = 'gfn3', type = 'any' | Initializer | FunctionExpr name = 'gfn3', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ExprStatement | CallExpr | Callee | GetFieldExpr '.' fieldName = 'gfn1' | Receiver | Id 'this' |-VarDecl 'local', name = 'gfn4', type = 'any' | Initializer | FunctionExpr 'lambda' name = '(all_syntax.nut:198)', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | LiteralExpr LK_STRING "lambda_str1" |-VarDecl 'let', name = 'gfn5', type = 'any' | Initializer | FunctionExpr 'lambda' name = 'gfn5', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | LiteralExpr LK_STRING "lambda_str2" |-VarDecl 'local', name = '_call', type = 'any' | Initializer | FunctionExpr name = '(all_syntax.nut:200)', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 1 | | Parameter #1 name = 'name', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | ExprStatement | CallExpr | Callee | GetSlotExpr '[' | |-Receiver | | Id 'this' | `-Key | Id 'name' |-VarDecl 'let', name = 'gfn_pure', type = 'any' | Initializer | FunctionExpr 'lambda' name = 'gfn5', pure = 1, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | LiteralExpr LK_STRING "lambda_str3" |-VarDecl 'let', name = 'gfn_nodiscard', type = 'any' | Initializer | FunctionExpr 'lambda' name = 'gfn6', pure = 0, nodiscard = 1, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | LiteralExpr LK_STRING "lambda_str4" |-VarDecl 'let', name = 'gfn_pure_nodiscard', type = 'any' | Initializer | FunctionExpr 'lambda' name = 'gfn7', pure = 1, nodiscard = 1, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | LiteralExpr LK_STRING "lambda_str5" |-VarDecl 'let', name = 'fa1', type = 'any' | Initializer | FunctionExpr name = 'fa1', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 1 | | Parameter #1 name = 'x', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | Id 'x' |-VarDecl 'let', name = 'fa2', type = 'any' | Initializer | FunctionExpr name = 'fa2', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 2 | | |-Parameter #1 name = 'x', type = 'any' | | `-Parameter #2 name = 'y', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '+' | |-Left | | Id 'x' | `-Right | Id 'y' |-VarDecl 'let', name = 'fa3', type = 'any' | Initializer | FunctionExpr name = 'fa3', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 3 | | |-Parameter #1 name = 'x', type = 'any' | | |-Parameter #2 name = 'y', type = 'any' | | `-Parameter #3 name = 'z', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '+' | |-Left | | Id 'x' | `-Right | BinExpr '*' | |-Left | | Id 'y' | `-Right | Id 'z' |-VarDecl 'let', name = 'fa4', type = 'any' | Initializer | FunctionExpr name = 'fa4', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 2 | | |-Parameter #1 name = 'x', type = 'any' | | |-Parameter #2 name = 'vargv', type = 'any' (vararg) | | `-Vararg... | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '+' | |-Left | | Id 'x' | `-Right | CallExpr | Callee | GetFieldExpr '.' fieldName = 'len' | Receiver | Id 'vargv' |-VarDecl 'let', name = 'fa5', type = 'any' | Initializer | FunctionExpr name = 'fa5', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 1 | | Parameter #1 name = 'x', type = 'any' (has default) | | DefaultValue | | ArrayExpr | | |-Element #1 | | | LiteralExpr LK_INT 1 | | `-Element #2 | | LiteralExpr LK_INT 2 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | GetSlotExpr '?[' | |-Receiver | | Id 'x' | `-Key | LiteralExpr LK_INT 1 |-VarDecl 'let', name = 'fa6', type = 'any' | Initializer | FunctionExpr name = 'fa6', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 2 | | |-Parameter #1 name = 'x', type = 'any' | | `-Parameter #2 name = 'y', type = 'any' (has default) | | DefaultValue | | LiteralExpr LK_NULL | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | Id 'y' |-VarDecl 'let', name = 'fa7', type = 'any' | Initializer | FunctionExpr name = 'fa7', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 2 | | |-Parameter #1 name = 'x', type = 'any' | | `-Parameter #2 name = 'y', type = 'any' (has default) | | DefaultValue | | TableExpr | | Field | | |-Key | | | LiteralExpr LK_STRING "a" | | `-Value | | LiteralExpr LK_INT 4 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | Id 'y' |-VarDecl 'let', name = 'fa8', type = 'any' | Initializer | FunctionExpr name = 'fa8', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 2 | | |-Parameter #1 name = 'x', type = 'any' | | `-Parameter #2 name = 'y', type = 'any' (has default) | | DefaultValue | | TableExpr | | Field | | |-Key | | | LiteralExpr LK_STRING "a" | | `-Value | | LiteralExpr LK_INT 5 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | Id 'y' |-VarDecl 'let', name = 'pure_fn', type = 'any' | Initializer | FunctionExpr name = 'pure_fn', pure = 1, nodiscard = 0, resultType = 'any' | |-Parameters count = 1 | | Parameter #1 name = 'x', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '*' | |-Left | | Id 'x' | `-Right | Id 'x' |-VarDecl 'let', name = 'pure_nodiscard_fn', type = 'any' | Initializer | FunctionExpr name = 'pure_nodiscard_fn', pure = 1, nodiscard = 1, resultType = 'any' | |-Parameters count = 1 | | Parameter #1 name = 'x', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '*' | |-Left | | Id 'x' | `-Right | Id 'x' |-VarDecl 'let', name = 'coroutine_fn', type = 'any' | Initializer | FunctionExpr name = 'coroutine_fn', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | |-YieldStatement | | LiteralExpr LK_INT 1100 | `-YieldStatement | LiteralExpr LK_INT 1200 |-ExprStatement | CallExpr | |-Callee | | Id 'println' | `-Arguments | Argument #1 | UnExpr 'RESUME' | CallExpr | Callee | Id 'coroutine_fn' |-VarDecl 'let', name = 'des1', type = 'any' | Initializer | FunctionExpr name = 'des1', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 1 | | Parameter #1 name = '@arg1', type = 'table|instance|class' | | Destructuring | | DestructuringDecl type = 'table' | | |-Initializer | | | Id '@arg1' | | `-VarDecl 'let', name = 'x', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '+' | |-Left | | Id 'x' | `-Right | LiteralExpr LK_INT 1 |-VarDecl 'let', name = 'des2', type = 'any' | Initializer | FunctionExpr name = 'des2', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 1 | | Parameter #1 name = '@arg1', type = 'array' | | Destructuring | | DestructuringDecl type = 'array' | | |-Initializer | | | Id '@arg1' | | `-VarDecl 'let', name = 'x', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '+' | |-Left | | Id 'x' | `-Right | LiteralExpr LK_INT 1 |-VarDecl 'let', name = 'des3', type = 'any' | Initializer | FunctionExpr name = 'des3', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 2 | | |-Parameter #1 name = '@arg1', type = 'array' | | | Destructuring | | | DestructuringDecl type = 'array' | | | |-Initializer | | | | Id '@arg1' | | | `-VarDecl 'let', name = 'x', type = 'any' | | `-Parameter #2 name = '@arg2', type = 'table|instance|class' | | Destructuring | | DestructuringDecl type = 'table' | | |-Initializer | | | Id '@arg2' | | `-VarDecl 'let', name = 'y', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '+' | |-Left | | Id 'x' | `-Right | Id 'y' |-VarDecl 'let', name = 'des4', type = 'any' | Initializer | FunctionExpr name = 'des4', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 2 | | |-Parameter #1 name = '@arg1', type = 'array' | | | Destructuring | | | DestructuringDecl type = 'array' | | | |-Initializer | | | | Id '@arg1' | | | |-VarDecl 'let', name = 'x', type = 'any' | | | |-VarDecl 'let', name = 'z', type = 'any' | | | `-VarDecl 'let', name = 'w', type = 'any' | | `-Parameter #2 name = '@arg2', type = 'table|instance|class' | | Destructuring | | DestructuringDecl type = 'table' | | |-Initializer | | | Id '@arg2' | | `-VarDecl 'let', name = 'y', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '+' | |-Left | | Id 'x' | `-Right | Id 'y' |-VarDecl 'let', name = 'des5', type = 'any' | Initializer | FunctionExpr name = 'des5', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 2 | | |-Parameter #1 name = 'x', type = 'any' | | `-Parameter #2 name = '@arg2', type = 'table|instance|class' | | Destructuring | | DestructuringDecl type = 'table' | | |-Initializer | | | Id '@arg2' | | `-VarDecl 'let', name = 'y', type = 'any' | | Initializer | | LiteralExpr LK_NULL | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '+' | |-Left | | Id 'x' | `-Right | UnExpr '(' | BinExpr '??' | |-Left | | Id 'y' | `-Right | LiteralExpr LK_INT 2 |-VarDecl 'let', name = 'des6', type = 'any' | Initializer | FunctionExpr name = 'des6', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 2 | | |-Parameter #1 name = 'x', type = 'any' | | `-Parameter #2 name = '@arg2', type = 'table|instance|class' | | Destructuring | | DestructuringDecl type = 'table' | | |-Initializer | | | Id '@arg2' | | |-VarDecl 'let', name = 'y', type = 'any' | | | Initializer | | | LiteralExpr LK_NULL | | `-VarDecl 'let', name = 'z', type = 'number' | | Initializer | | LiteralExpr LK_INT 1000 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '+' | |-Left | | BinExpr '+' | | |-Left | | | Id 'x' | | `-Right | | UnExpr '(' | | BinExpr '??' | | |-Left | | | Id 'y' | | `-Right | | LiteralExpr LK_INT 2 | `-Right | Id 'z' |-VarDecl 'let', name = '$ch0', type = 'any' | Initializer | FunctionExpr 'lambda' name = '(all_syntax.nut:230)', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | LiteralExpr LK_NULL |-VarDecl 'let', name = '$ch1', type = 'any' | Initializer | FunctionExpr 'lambda' name = '(all_syntax.nut:230)', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | Id '$ch0' |-VarDecl 'let', name = '$ch2', type = 'any' | Initializer | FunctionExpr 'lambda' name = '(all_syntax.nut:230)', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | Id '$ch1' |-VarDecl 'let', name = 'des7', type = 'any' | Initializer | FunctionExpr name = 'des7', pure = 1, nodiscard = 1, resultType = 'any' | |-Parameters count = 3 | | |-Parameter #1 name = '@arg1', type = 'array' | | | Destructuring | | | DestructuringDecl type = 'array' | | | |-Initializer | | | | Id '@arg1' | | | |-VarDecl 'let', name = 'x', type = 'function' | | | | Initializer | | | | Id '$ch2' | | | |-VarDecl 'let', name = 't', type = 'any' | | | |-VarDecl 'let', name = 'r', type = 'string' | | | | Initializer | | | | LiteralExpr LK_STRING "abc555" | | | `-VarDecl 'let', name = 'g', type = 'any' | | | Initializer | | | LiteralExpr LK_STRING "abc666" | | |-Parameter #2 name = '@arg2', type = 'table|instance|class' | | | Destructuring | | | DestructuringDecl type = 'table' | | | |-Initializer | | | | Id '@arg2' | | | |-VarDecl 'let', name = 'y', type = 'any' | | | | Initializer | | | | LiteralExpr LK_NULL | | | `-VarDecl 'let', name = 'z', type = 'number' | | | Initializer | | | LiteralExpr LK_INT 1000 | | |-Parameter #3 name = 'vargv', type = 'any' (vararg) | | `-Vararg... | `-Body | Block isRoot = 0, isBody = 1 | |-DestructuringDecl type = 'table' | | |-Initializer | | | Id 't' | | |-VarDecl 'let', name = 'p', type = 'any' | | `-VarDecl 'let', name = 'i', type = 'any' | |-ExprStatement | | CallExpr | | Callee | | Id 'println' | `-ReturnStatement | BinExpr '+' | |-Left | | BinExpr '+' | | |-Left | | | Id 'x' | | `-Right | | UnExpr '(' | | BinExpr '??' | | |-Left | | | Id 'y' | | `-Right | | LiteralExpr LK_INT 2 | `-Right | Id 'z' |-VarDecl 'let', name = 'i0', type = 'any' | Initializer | BinExpr '+' | |-Left | | LiteralExpr LK_INT 1 | `-Right | LiteralExpr LK_INT 2 |-VarDecl 'let', name = 'i1', type = 'any' | Initializer | BinExpr '-' | |-Left | | LiteralExpr LK_INT 1 | `-Right | LiteralExpr LK_INT 2 |-VarDecl 'let', name = 'i2', type = 'any' | Initializer | BinExpr '*' | |-Left | | LiteralExpr LK_INT 1 | `-Right | LiteralExpr LK_INT 2 |-VarDecl 'let', name = 'i3', type = 'any' | Initializer | BinExpr '/' | |-Left | | LiteralExpr LK_INT 1 | `-Right | LiteralExpr LK_INT 2 |-VarDecl 'let', name = 'i4', type = 'any' | Initializer | BinExpr '%' | |-Left | | LiteralExpr LK_INT 1 | `-Right | LiteralExpr LK_INT 2 |-VarDecl 'let', name = 'i5', type = 'any' | Initializer | BinExpr '^' | |-Left | | LiteralExpr LK_INT 1 | `-Right | LiteralExpr LK_INT 2 |-VarDecl 'let', name = 'i6', type = 'any' | Initializer | BinExpr '&' | |-Left | | LiteralExpr LK_INT 1 | `-Right | LiteralExpr LK_INT 2 |-VarDecl 'let', name = 'i7', type = 'any' | Initializer | BinExpr '|' | |-Left | | LiteralExpr LK_INT 1 | `-Right | LiteralExpr LK_INT 2 |-VarDecl 'let', name = 'i8', type = 'any' | Initializer | BinExpr '||' | |-Left | | LiteralExpr LK_INT 1 | `-Right | LiteralExpr LK_INT 2 |-VarDecl 'let', name = 'i9', type = 'any' | Initializer | BinExpr '&&' | |-Left | | LiteralExpr LK_INT 1 | `-Right | LiteralExpr LK_INT 2 |-VarDecl 'let', name = 'i10', type = 'any' | Initializer | BinExpr '==' | |-Left | | LiteralExpr LK_INT 1 | `-Right | LiteralExpr LK_INT 2 |-VarDecl 'let', name = 'i11', type = 'any' | Initializer | BinExpr '!=' | |-Left | | LiteralExpr LK_INT 1 | `-Right | LiteralExpr LK_INT 2 |-VarDecl 'let', name = 'i12', type = 'any' | Initializer | BinExpr '<' | |-Left | | LiteralExpr LK_INT 1 | `-Right | LiteralExpr LK_INT 2 |-VarDecl 'let', name = 'i13', type = 'any' | Initializer | BinExpr '>' | |-Left | | LiteralExpr LK_INT 1 | `-Right | LiteralExpr LK_INT 2 |-VarDecl 'let', name = 'i14', type = 'any' | Initializer | BinExpr '<=' | |-Left | | LiteralExpr LK_INT 1 | `-Right | LiteralExpr LK_INT 2 |-VarDecl 'let', name = 'i15', type = 'any' | Initializer | BinExpr '>=' | |-Left | | LiteralExpr LK_INT 1 | `-Right | LiteralExpr LK_INT 2 |-VarDecl 'let', name = 'i16', type = 'any' | Initializer | BinExpr '<=>' | |-Left | | LiteralExpr LK_INT 1 | `-Right | LiteralExpr LK_INT 2 |-VarDecl 'let', name = 'i17', type = 'any' | Initializer | UnExpr '-' | UnExpr '(' | LiteralExpr LK_INT 123 |-VarDecl 'let', name = 'i18', type = 'any' | Initializer | UnExpr '~' | UnExpr '(' | LiteralExpr LK_INT 123 |-VarDecl 'let', name = 'i19', type = 'any' | Initializer | UnExpr '!' | UnExpr '(' | LiteralExpr LK_INT 123 |-VarDecl 'let', name = 'i20', type = 'any' | Initializer | UnExpr 'TYPEOF' | LiteralExpr LK_INT 123 |-VarDecl 'let', name = 'i21', type = 'any' | Initializer | UnExpr 'INLINE_CONST' | LiteralExpr LK_INT 123 |-VarDecl 'let', name = 'i22', type = 'any' | Initializer | UnExpr 'STATIC_MEMO' | LiteralExpr LK_INT 123 |-VarDecl 'let', name = 'i23', type = 'any' | Initializer | BinExpr 'IN' | |-Left | | LiteralExpr LK_STRING "x" | `-Right | TableExpr | Field | |-Key | | LiteralExpr LK_STRING "x" | `-Value | LiteralExpr LK_INT 5 |-VarDecl 'let', name = 'i24', type = 'any' | Initializer | UnExpr '!' | BinExpr 'IN' | |-Left | | LiteralExpr LK_STRING "x" | `-Right | TableExpr | Field | |-Key | | LiteralExpr LK_STRING "x" | `-Value | LiteralExpr LK_INT 6 |-VarDecl 'let', name = 'i25', type = 'any' | Initializer | BinExpr '>>' | |-Left | | LiteralExpr LK_INT 1 | `-Right | LiteralExpr LK_INT 2 |-VarDecl 'let', name = 'i26', type = 'any' | Initializer | BinExpr '<<' | |-Left | | LiteralExpr LK_INT 1 | `-Right | LiteralExpr LK_INT 2 |-VarDecl 'let', name = 'i27', type = 'any' | Initializer | BinExpr '>>>' | |-Left | | LiteralExpr LK_INT 1 | `-Right | LiteralExpr LK_INT 2 |-VarDecl 'let', name = 'i28', type = 'any' | Initializer | BinExpr '??' | |-Left | | LiteralExpr LK_INT 1 | `-Right | LiteralExpr LK_INT 2 |-VarDecl 'local', name = 'iv', type = 'any' | Initializer | LiteralExpr LK_INT 123 |-ExprStatement | IncExpr IF_POSTFIX '++' | Id 'iv' |-ExprStatement | IncExpr IF_POSTFIX '--' | Id 'iv' |-ExprStatement | IncExpr IF_PREFIX '--' | Id 'iv' |-ExprStatement | IncExpr IF_PREFIX '++' | Id 'iv' |-ExprStatement | BinExpr '+=' | |-Left | | Id 'iv' | `-Right | LiteralExpr LK_INT 123 |-ExprStatement | BinExpr '-=' | |-Left | | Id 'iv' | `-Right | LiteralExpr LK_INT 123 |-ExprStatement | BinExpr '*=' | |-Left | | Id 'iv' | `-Right | LiteralExpr LK_INT 123 |-ExprStatement | BinExpr '/=' | |-Left | | Id 'iv' | `-Right | LiteralExpr LK_INT 123 |-ExprStatement | BinExpr '%=' | |-Left | | Id 'iv' | `-Right | LiteralExpr LK_INT 123 |-IfStatement | |-Condition | | BaseExpr | `-ThenBranch | Block isRoot = 0, isBody = 0 | ExprStatement | CallExpr | |-Callee | | Id 'println' | `-Arguments | Argument #1 | LiteralExpr LK_STRING "base != null" |-Block isRoot = 0, isBody = 0 | |-EmptyStatement | |-EmptyStatement | |-EmptyStatement | |-EmptyStatement | |-EmptyStatement | `-EmptyStatement |-IfStatement | |-Condition | | BinExpr '==' | | |-Left | | | LiteralExpr LK_INT 701 | | `-Right | | LiteralExpr LK_INT 701 | `-ThenBranch | Block isRoot = 0, isBody = 0 | ExprStatement | CallExpr | |-Callee | | Id 'fa1' | `-Arguments | Argument #1 | LiteralExpr LK_INT 701 |-IfStatement | |-Condition | | BinExpr '==' | | |-Left | | | LiteralExpr LK_INT 702 | | `-Right | | LiteralExpr LK_INT 703 | |-ThenBranch | | Block isRoot = 0, isBody = 0 | | ExprStatement | | CallExpr | | |-Callee | | | Id 'fa1' | | `-Arguments | | Argument #1 | | LiteralExpr LK_INT 702 | `-ElseBranch | Block isRoot = 0, isBody = 0 | ExprStatement | CallExpr | |-Callee | | Id 'fa2' | `-Arguments | |-Argument #1 | | LiteralExpr LK_INT 703 | `-Argument #2 | LiteralExpr LK_INT 704 |-IfStatement | |-Condition | | BinExpr '==' | | |-Left | | | LiteralExpr LK_INT 705 | | `-Right | | LiteralExpr LK_INT 706 | |-ThenBranch | | Block isRoot = 0, isBody = 0 | | ExprStatement | | CallExpr | | |-Callee | | | Id 'fa1' | | `-Arguments | | Argument #1 | | LiteralExpr LK_INT 707 | `-ElseBranch | Block isRoot = 0, isBody = 0 | IfStatement | |-Condition | | BinExpr '==' | | |-Left | | | LiteralExpr LK_INT 708 | | `-Right | | LiteralExpr LK_INT 709 | |-ThenBranch | | Block isRoot = 0, isBody = 0 | | ExprStatement | | CallExpr | | |-Callee | | | Id 'fa2' | | `-Arguments | | |-Argument #1 | | | LiteralExpr LK_INT 710 | | `-Argument #2 | | LiteralExpr LK_INT 711 | `-ElseBranch | Block isRoot = 0, isBody = 0 | ExprStatement | CallExpr | |-Callee | | Id 'fa2' | `-Arguments | |-Argument #1 | | LiteralExpr LK_INT 712 | `-Argument #2 | LiteralExpr LK_INT 713 |-Block isRoot = 0, isBody = 0 | |-VarDecl 'local', name = 'cv', type = 'any' | | Initializer | | Id 'iv' | `-IfStatement | |-Condition | | Id 'cv' | `-ThenBranch | Block isRoot = 0, isBody = 0 | ExprStatement | CallExpr | |-Callee | | Id 'println' | `-Arguments | Argument #1 | Id 'cv' |-Block isRoot = 0, isBody = 0 | |-VarDecl 'local', name = 'cv', type = 'int|null' | | Initializer | | Id 'iv' | `-IfStatement | |-Condition | | Id 'cv' | |-ThenBranch | | Block isRoot = 0, isBody = 0 | | ExprStatement | | CallExpr | | |-Callee | | | Id 'println' | | `-Arguments | | Argument #1 | | Id 'cv' | `-ElseBranch | Block isRoot = 0, isBody = 0 | ExprStatement | CallExpr | |-Callee | | Id 'println' | `-Arguments | Argument #1 | BinExpr '-' | |-Left | | Id 'cv' | `-Right | LiteralExpr LK_INT 1 |-Block isRoot = 0, isBody = 0 | |-VarDecl 'let', name = 'cv', type = 'int' | | Initializer | | Id 'iv' | `-IfStatement | |-Condition | | BinExpr '>' | | |-Left | | | Id 'cv' | | `-Right | | LiteralExpr LK_INT -1000000 | `-ThenBranch | Block isRoot = 0, isBody = 0 | ExprStatement | CallExpr | |-Callee | | Id 'println' | `-Arguments | Argument #1 | Id 'cv' |-Block isRoot = 0, isBody = 0 | |-VarDecl 'local', name = 'cv', type = 'any' | | Initializer | | Id 'iv' | `-IfStatement | |-Condition | | Id 'cv' | |-ThenBranch | | Block isRoot = 0, isBody = 0 | | ExprStatement | | CallExpr | | |-Callee | | | Id 'println' | | `-Arguments | | Argument #1 | | Id 'cv' | `-ElseBranch | Block isRoot = 0, isBody = 0 | Block isRoot = 0, isBody = 0 | |-VarDecl 'local', name = 'cv2', type = 'any' | | Initializer | | Id 'iv' | `-IfStatement | |-Condition | | Id 'cv2' | `-ThenBranch | Block isRoot = 0, isBody = 0 | ExprStatement | CallExpr | |-Callee | | Id 'println' | `-Arguments | Argument #1 | LiteralExpr LK_STRING "fail" |-WhileStatement | |-Condition | | IncExpr IF_POSTFIX '--' | | Id 'iv' | `-Body | Block isRoot = 0, isBody = 0 | ExprStatement | CallExpr | |-Callee | | Id 'fa1' | `-Arguments | Argument #1 | LiteralExpr LK_INT 714 |-DoWhileStatement | |-Body | | Block isRoot = 0, isBody = 0 | | ExprStatement | | CallExpr | | |-Callee | | | Id 'fa1' | | `-Arguments | | Argument #1 | | LiteralExpr LK_INT 715 | `-Condition | BinExpr '<' | |-Left | | IncExpr IF_POSTFIX '++' | | Id 'iv' | `-Right | LiteralExpr LK_INT 3 |-ForeachStatement | |-ValueVariable | | VarDecl 'let', name = 'i', type = 'any' | |-Container | | Id 'e1' | `-Body | Block isRoot = 0, isBody = 0 | ExprStatement | CallExpr | |-Callee | | Id 'fa1' | `-Arguments | Argument #1 | Id 'i' |-ForeachStatement | |-IndexVariable | | VarDecl 'let', name = 'k', type = 'any' | |-ValueVariable | | VarDecl 'let', name = 'v', type = 'any' | |-Container | | Id 't7' | `-Body | Block isRoot = 0, isBody = 0 | ExprStatement | CallExpr | |-Callee | | Id 'fa2' | `-Arguments | |-Argument #1 | | Id 'k' | `-Argument #2 | Id 'v' |-ForStatement | Body | Block isRoot = 0, isBody = 0 | BreakStatement |-ForStatement | |-Initializer | | VarDecl 'local', name = 'iter', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-Condition | | BinExpr '<' | | |-Left | | | Id 'iter' | | `-Right | | LiteralExpr LK_INT 4 | |-Modifier | | IncExpr IF_PREFIX '++' | | Id 'iter' | `-Body | Block isRoot = 0, isBody = 0 | ContinueStatement |-ForStatement | |-Initializer | | VarDecl 'local', name = 'iter', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-Condition | | BinExpr '<' | | |-Left | | | Id 'iter' | | `-Right | | LiteralExpr LK_INT 4 | |-Modifier | | CommaExpr | | |-Expression #1 | | | BinExpr '+=' | | | |-Left | | | | Id 'iter' | | | `-Right | | | LiteralExpr LK_INT 2 | | `-Expression #2 | | IncExpr IF_POSTFIX '--' | | Id 'iter' | `-Body | Block isRoot = 0, isBody = 0 | ContinueStatement |-TryStatement | |-TryBlock | | ExprStatement | | CallExpr | | |-Callee | | | Id 'fa1' | | `-Arguments | | Argument #1 | | LiteralExpr LK_INT 716 | `-CatchBlock exceptionId = 'e' | ExprStatement | CallExpr | |-Callee | | Id 'fa1' | `-Arguments | Argument #1 | LiteralExpr LK_INT 717 |-TryStatement | |-TryBlock | | ThrowStatement | | LiteralExpr LK_NULL | `-CatchBlock exceptionId = 'e' | ExprStatement | CallExpr | |-Callee | | Id 'fa1' | `-Arguments | Argument #1 | LiteralExpr LK_INT 718 |-DirectiveStmt |-VarDecl 'local', name = 'w', type = 'any' | Initializer | CallExpr | Callee | Id 'getlocals' |-VarDecl 'local', name = 'res', type = 'any' | Initializer | ArrayExpr |-ForeachStatement | |-IndexVariable | | VarDecl 'let', name = 'k', type = 'any' | |-ValueVariable | | VarDecl 'let', name = 'v', type = 'any' | |-Container | | Id 'w' | `-Body | Block isRoot = 0, isBody = 0 | ExprStatement | CallExpr | |-Callee | | GetFieldExpr '.' fieldName = 'append' | | Receiver | | Id 'res' | `-Arguments | Argument #1 | CallExpr | |-Callee | | GetFieldExpr '.' fieldName = 'subst' | | Receiver | | LiteralExpr LK_STRING "{0} = {1}" | `-Arguments | |-Argument #1 | | Id 'k' | `-Argument #2 | TerExpr | |-Condition | | BinExpr '>=' | | |-Left | | | CallExpr | | | |-Callee | | | | GetFieldExpr '.' fieldName = 'indexof' | | | | Receiver | | | | ArrayExpr | | | | |-Element #1 | | | | | LiteralExpr LK_STRING "integer" | | | | |-Element #2 | | | | | LiteralExpr LK_STRING "bool" | | | | |-Element #3 | | | | | LiteralExpr LK_STRING "string" | | | | `-Element #4 | | | | LiteralExpr LK_STRING "float" | | | `-Arguments | | | Argument #1 | | | CallExpr | | | |-Callee | | | | Id 'type' | | | `-Arguments | | | Argument #1 | | | Id 'v' | | `-Right | | LiteralExpr LK_INT 0 | |-TrueBranch | | Id 'v' | `-FalseBranch | CallExpr | |-Callee | | Id 'type' | `-Arguments | Argument #1 | Id 'v' |-ExprStatement | CallExpr | Callee | GetFieldExpr '.' fieldName = 'sort' | Receiver | Id 'res' |-ForeachStatement | |-IndexVariable | | VarDecl 'let', name = '_', type = 'any' | |-ValueVariable | | VarDecl 'let', name = 'v', type = 'any' | |-Container | | Id 'res' | `-Body | Block isRoot = 0, isBody = 0 | ExprStatement | CallExpr | |-Callee | | Id 'println' | `-Arguments | Argument #1 | Id 'v' |-VarDecl 'let', name = 'A', type = 'any' | Initializer | ClassExpr | |-Key: | `-Members | FunctionExpr name = 'x', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | LiteralExpr LK_INT 0 |-VarDecl 'let', name = 'instance_a', type = 'any' | Initializer | CallExpr | Callee | Id 'A' |-VarDecl 'let', name = '$ch3', type = 'any' | Initializer | FunctionExpr name = '(all_syntax.nut:396)', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | |-EmptyStatement | `-ReturnStatement | LiteralExpr LK_INT 1234 |-VarDecl 'let', name = 'test', type = 'any' | Initializer | FunctionExpr name = 'test', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | TableExpr | |-Field | | |-Key | | | LiteralExpr LK_STRING "x" | | `-Value | | LiteralExpr LK_INT 4 | `-Field | |-Key | | LiteralExpr LK_STRING "fn" | `-Value | Id '$ch3' |-ExprStatement | CallExpr | |-Callee | | Id 'println' | `-Arguments | Argument #1 | CallExpr | |-Callee | | Id 'doc' | `-Arguments | Argument #1 | Id 'A' |-ExprStatement | CallExpr | |-Callee | | Id 'println' | `-Arguments | Argument #1 | CallExpr | |-Callee | | Id 'doc' | `-Arguments | Argument #1 | Id 'instance_a' |-ExprStatement | CallExpr | |-Callee | | Id 'println' | `-Arguments | Argument #1 | CallExpr | |-Callee | | Id 'doc' | `-Arguments | Argument #1 | CallExpr | Callee | Id 'test' |-ExprStatement | CallExpr | |-Callee | | Id 'println' | `-Arguments | Argument #1 | CallExpr | |-Callee | | Id 'doc' | `-Arguments | Argument #1 | GetFieldExpr '.' fieldName = 'fn' | Receiver | CallExpr | Callee | Id 'test' `-ExprStatement CallExpr |-Callee | Id 'println' `-Arguments Argument #1 LiteralExpr LK_STRING "done" ================================================ FILE: testData/ast/optimizations/closureHoisting/classCrossFunc.nut ================================================ local z = 0 function foo(x, y) { return function() { let c = class { x = 10 function zed() { return function () { return x + 10; }() + this.x } } return c().zed() } } ================================================ FILE: testData/ast/optimizations/closureHoisting/classCrossFunc.opt.txt ================================================ Block isRoot = 1, isBody = 0 |-VarDecl 'local', name = 'z', type = 'any' | Initializer | LiteralExpr LK_INT 0 `-VarDecl 'let', name = 'foo', type = 'any' Initializer FunctionExpr name = 'foo', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 2 | |-Parameter #1 name = 'x', type = 'any' | `-Parameter #2 name = 'y', type = 'any' `-Body Block isRoot = 0, isBody = 1 |-VarDecl 'let', name = '$ch0', type = 'any' | Initializer | FunctionExpr name = '(classCrossFunc.nut:7)', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '+' | |-Left | | Id 'x' | `-Right | LiteralExpr LK_INT 10 `-ReturnStatement FunctionExpr name = '(classCrossFunc.nut:3)', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 0 `-Body Block isRoot = 0, isBody = 1 |-VarDecl 'let', name = 'c', type = 'any' | Initializer | ClassExpr | |-Key: | `-Members | |-LiteralExpr LK_INT 10 | `-FunctionExpr name = 'zed', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '+' | |-Left | | CallExpr | | Callee | | Id '$ch0' | `-Right | GetFieldExpr '.' fieldName = 'x' | Receiver | Id 'this' `-ReturnStatement CallExpr Callee GetFieldExpr '.' fieldName = 'zed' Receiver CallExpr Callee Id 'c' ================================================ FILE: testData/ast/optimizations/closureHoisting/classSimple.nut ================================================ function foo(x, y) { let c = class { x = 10 function zed() { return function () { return x + 10; }() + this.x } } return c().zed() } ================================================ FILE: testData/ast/optimizations/closureHoisting/classSimple.opt.txt ================================================ Block isRoot = 1, isBody = 0 VarDecl 'let', name = 'foo', type = 'any' Initializer FunctionExpr name = 'foo', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 2 | |-Parameter #1 name = 'x', type = 'any' | `-Parameter #2 name = 'y', type = 'any' `-Body Block isRoot = 0, isBody = 1 |-VarDecl 'let', name = '$ch0', type = 'any' | Initializer | FunctionExpr name = '(classSimple.nut:6)', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '+' | |-Left | | Id 'x' | `-Right | LiteralExpr LK_INT 10 |-VarDecl 'let', name = 'c', type = 'any' | Initializer | ClassExpr | |-Key: | `-Members | |-LiteralExpr LK_INT 10 | `-FunctionExpr name = 'zed', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '+' | |-Left | | CallExpr | | Callee | | Id '$ch0' | `-Right | GetFieldExpr '.' fieldName = 'x' | Receiver | Id 'this' `-ReturnStatement CallExpr Callee GetFieldExpr '.' fieldName = 'zed' Receiver CallExpr Callee Id 'c' ================================================ FILE: testData/ast/optimizations/closureHoisting/constDep.nut ================================================ function foo() { const x = "asdf" println(x) function bar() { let a = x } bar() } ================================================ FILE: testData/ast/optimizations/closureHoisting/constDep.opt.txt ================================================ Block isRoot = 1, isBody = 0 VarDecl 'let', name = 'foo', type = 'any' Initializer FunctionExpr name = 'foo', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 0 `-Body Block isRoot = 0, isBody = 1 |-ConstDecl name = 'x', isGlobal = 0 | LiteralExpr LK_STRING "asdf" |-ExprStatement | CallExpr | |-Callee | | Id 'println' | `-Arguments | Argument #1 | Id 'x' |-VarDecl 'let', name = 'bar', type = 'any' | Initializer | FunctionExpr name = 'bar', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | VarDecl 'let', name = 'a', type = 'any' | Initializer | Id 'x' `-ExprStatement CallExpr Callee Id 'bar' ================================================ FILE: testData/ast/optimizations/closureHoisting/constFunc.nut ================================================ // const function declarations should not be hoisted because they are // compile-time constants. Hoisting would replace the FunctionExpr with // an Id reference ($chN) which is not a compile-time constant. function outer() { function inner() { const function [pure] constFn(a, b) { return a + b } const result = constFn(1, 2) // non-const function in the same scope should still be hoisted let regularFn = function(x) { return x * 2 } let r = regularFn(3) } inner() } ================================================ FILE: testData/ast/optimizations/closureHoisting/constFunc.opt.txt ================================================ Block isRoot = 1, isBody = 0 |-VarDecl 'let', name = '$ch0', type = 'any' | Initializer | FunctionExpr name = 'regularFn', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 1 | | Parameter #1 name = 'x', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '*' | |-Left | | Id 'x' | `-Right | LiteralExpr LK_INT 2 |-VarDecl 'let', name = '$ch1', type = 'any' | Initializer | FunctionExpr name = 'inner', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | |-ConstDecl name = 'constFn', isGlobal = 0 | | UnExpr 'INLINE_CONST' | | FunctionExpr name = 'constFn', pure = 1, nodiscard = 0, resultType = 'any' | | |-Parameters count = 2 | | | |-Parameter #1 name = 'a', type = 'any' | | | `-Parameter #2 name = 'b', type = 'any' | | `-Body | | Block isRoot = 0, isBody = 1 | | ReturnStatement | | BinExpr '+' | | |-Left | | | Id 'a' | | `-Right | | Id 'b' | |-ConstDecl name = 'result', isGlobal = 0 | | CallExpr | | |-Callee | | | Id 'constFn' | | `-Arguments | | |-Argument #1 | | | LiteralExpr LK_INT 1 | | `-Argument #2 | | LiteralExpr LK_INT 2 | |-VarDecl 'let', name = 'regularFn', type = 'any' | | Initializer | | Id '$ch0' | `-VarDecl 'let', name = 'r', type = 'any' | Initializer | CallExpr | |-Callee | | Id 'regularFn' | `-Arguments | Argument #1 | LiteralExpr LK_INT 3 `-VarDecl 'let', name = 'outer', type = 'any' Initializer FunctionExpr name = 'outer', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 0 `-Body Block isRoot = 0, isBody = 1 |-VarDecl 'let', name = 'inner', type = 'any' | Initializer | Id '$ch1' `-ExprStatement CallExpr Callee Id 'inner' ================================================ FILE: testData/ast/optimizations/closureHoisting/deepUnchained.nut ================================================ local z = 0 function foo(x, y) { return function(a, b) { z = a + b return function(c, d) { z = c - d return function(e, f) { z = e * f return function(g, h) { return g / h } } } } } ================================================ FILE: testData/ast/optimizations/closureHoisting/deepUnchained.opt.txt ================================================ Block isRoot = 1, isBody = 0 |-VarDecl 'local', name = 'z', type = 'any' | Initializer | LiteralExpr LK_INT 0 |-VarDecl 'let', name = '$ch0', type = 'any' | Initializer | FunctionExpr name = '(deepUnchained.nut:9)', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 2 | | |-Parameter #1 name = 'g', type = 'any' | | `-Parameter #2 name = 'h', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '/' | |-Left | | Id 'g' | `-Right | Id 'h' |-VarDecl 'let', name = '$ch1', type = 'any' | Initializer | FunctionExpr name = '(deepUnchained.nut:7)', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 2 | | |-Parameter #1 name = 'e', type = 'any' | | `-Parameter #2 name = 'f', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | |-ExprStatement | | BinExpr '=' | | |-Left | | | Id 'z' | | `-Right | | BinExpr '*' | | |-Left | | | Id 'e' | | `-Right | | Id 'f' | `-ReturnStatement | Id '$ch0' |-VarDecl 'let', name = '$ch2', type = 'any' | Initializer | FunctionExpr name = '(deepUnchained.nut:5)', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 2 | | |-Parameter #1 name = 'c', type = 'any' | | `-Parameter #2 name = 'd', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | |-ExprStatement | | BinExpr '=' | | |-Left | | | Id 'z' | | `-Right | | BinExpr '-' | | |-Left | | | Id 'c' | | `-Right | | Id 'd' | `-ReturnStatement | Id '$ch1' |-VarDecl 'let', name = '$ch3', type = 'any' | Initializer | FunctionExpr name = '(deepUnchained.nut:3)', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 2 | | |-Parameter #1 name = 'a', type = 'any' | | `-Parameter #2 name = 'b', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | |-ExprStatement | | BinExpr '=' | | |-Left | | | Id 'z' | | `-Right | | BinExpr '+' | | |-Left | | | Id 'a' | | `-Right | | Id 'b' | `-ReturnStatement | Id '$ch2' `-VarDecl 'let', name = 'foo', type = 'any' Initializer FunctionExpr name = 'foo', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 2 | |-Parameter #1 name = 'x', type = 'any' | `-Parameter #2 name = 'y', type = 'any' `-Body Block isRoot = 0, isBody = 1 ReturnStatement Id '$ch3' ================================================ FILE: testData/ast/optimizations/closureHoisting/externalSymbol.nut ================================================ function foo(x, y) { return function(a, b) { println(a + b) return function(c, d) { println(c - d) return function(e, f) { println(e * f) return function(g, h) { println(g / h) } } } } } ================================================ FILE: testData/ast/optimizations/closureHoisting/externalSymbol.opt.txt ================================================ Block isRoot = 1, isBody = 0 |-VarDecl 'let', name = '$ch0', type = 'any' | Initializer | FunctionExpr name = '(externalSymbol.nut:8)', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 2 | | |-Parameter #1 name = 'g', type = 'any' | | `-Parameter #2 name = 'h', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | ExprStatement | CallExpr | |-Callee | | Id 'println' | `-Arguments | Argument #1 | BinExpr '/' | |-Left | | Id 'g' | `-Right | Id 'h' |-VarDecl 'let', name = '$ch1', type = 'any' | Initializer | FunctionExpr name = '(externalSymbol.nut:6)', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 2 | | |-Parameter #1 name = 'e', type = 'any' | | `-Parameter #2 name = 'f', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | |-ExprStatement | | CallExpr | | |-Callee | | | Id 'println' | | `-Arguments | | Argument #1 | | BinExpr '*' | | |-Left | | | Id 'e' | | `-Right | | Id 'f' | `-ReturnStatement | Id '$ch0' |-VarDecl 'let', name = '$ch2', type = 'any' | Initializer | FunctionExpr name = '(externalSymbol.nut:4)', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 2 | | |-Parameter #1 name = 'c', type = 'any' | | `-Parameter #2 name = 'd', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | |-ExprStatement | | CallExpr | | |-Callee | | | Id 'println' | | `-Arguments | | Argument #1 | | BinExpr '-' | | |-Left | | | Id 'c' | | `-Right | | Id 'd' | `-ReturnStatement | Id '$ch1' |-VarDecl 'let', name = '$ch3', type = 'any' | Initializer | FunctionExpr name = '(externalSymbol.nut:2)', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 2 | | |-Parameter #1 name = 'a', type = 'any' | | `-Parameter #2 name = 'b', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | |-ExprStatement | | CallExpr | | |-Callee | | | Id 'println' | | `-Arguments | | Argument #1 | | BinExpr '+' | | |-Left | | | Id 'a' | | `-Right | | Id 'b' | `-ReturnStatement | Id '$ch2' `-VarDecl 'let', name = 'foo', type = 'any' Initializer FunctionExpr name = 'foo', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 2 | |-Parameter #1 name = 'x', type = 'any' | `-Parameter #2 name = 'y', type = 'any' `-Body Block isRoot = 0, isBody = 1 ReturnStatement Id '$ch3' ================================================ FILE: testData/ast/optimizations/closureHoisting/implicitChainedFuncs.nut ================================================ local z = 0 function foo(x, y) { return function bar(a, b) { //<< this function does not use anything from outer scope return function qux(c, d) { //<< but this function does and coupled with `bar` function z = x + a - c; } } } ================================================ FILE: testData/ast/optimizations/closureHoisting/implicitChainedFuncs.opt.txt ================================================ Block isRoot = 1, isBody = 0 |-VarDecl 'local', name = 'z', type = 'any' | Initializer | LiteralExpr LK_INT 0 `-VarDecl 'let', name = 'foo', type = 'any' Initializer FunctionExpr name = 'foo', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 2 | |-Parameter #1 name = 'x', type = 'any' | `-Parameter #2 name = 'y', type = 'any' `-Body Block isRoot = 0, isBody = 1 ReturnStatement FunctionExpr name = 'bar', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 2 | |-Parameter #1 name = 'a', type = 'any' | `-Parameter #2 name = 'b', type = 'any' `-Body Block isRoot = 0, isBody = 1 ReturnStatement FunctionExpr name = 'qux', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 2 | |-Parameter #1 name = 'c', type = 'any' | `-Parameter #2 name = 'd', type = 'any' `-Body Block isRoot = 0, isBody = 1 ExprStatement BinExpr '=' |-Left | Id 'z' `-Right BinExpr '-' |-Left | BinExpr '+' | |-Left | | Id 'x' | `-Right | Id 'a' `-Right Id 'c' ================================================ FILE: testData/ast/optimizations/closureHoisting/implicitChainedFuncs2.nut ================================================ local z = 0 function foo(x, y) { return function(a, b) { let fff = function() { return z } return function(c, d) { z = x + a - c; return function(f, g) { return f - g - x } } } } ================================================ FILE: testData/ast/optimizations/closureHoisting/implicitChainedFuncs2.opt.txt ================================================ Block isRoot = 1, isBody = 0 |-VarDecl 'local', name = 'z', type = 'any' | Initializer | LiteralExpr LK_INT 0 |-VarDecl 'let', name = '$ch1', type = 'any' | Initializer | FunctionExpr name = 'fff', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | Id 'z' `-VarDecl 'let', name = 'foo', type = 'any' Initializer FunctionExpr name = 'foo', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 2 | |-Parameter #1 name = 'x', type = 'any' | `-Parameter #2 name = 'y', type = 'any' `-Body Block isRoot = 0, isBody = 1 |-VarDecl 'let', name = '$ch0', type = 'any' | Initializer | FunctionExpr name = '(implicitChainedFuncs2.nut:10)', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 2 | | |-Parameter #1 name = 'f', type = 'any' | | `-Parameter #2 name = 'g', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '-' | |-Left | | BinExpr '-' | | |-Left | | | Id 'f' | | `-Right | | Id 'g' | `-Right | Id 'x' `-ReturnStatement FunctionExpr name = '(implicitChainedFuncs2.nut:4)', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 2 | |-Parameter #1 name = 'a', type = 'any' | `-Parameter #2 name = 'b', type = 'any' `-Body Block isRoot = 0, isBody = 1 |-VarDecl 'let', name = 'fff', type = 'any' | Initializer | Id '$ch1' `-ReturnStatement FunctionExpr name = '(implicitChainedFuncs2.nut:8)', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 2 | |-Parameter #1 name = 'c', type = 'any' | `-Parameter #2 name = 'd', type = 'any' `-Body Block isRoot = 0, isBody = 1 |-ExprStatement | BinExpr '=' | |-Left | | Id 'z' | `-Right | BinExpr '-' | |-Left | | BinExpr '+' | | |-Left | | | Id 'x' | | `-Right | | Id 'a' | `-Right | Id 'c' `-ReturnStatement Id '$ch0' ================================================ FILE: testData/ast/optimizations/closureHoisting/indirectLocalCapture.nut ================================================ // Test: closure captures variable from nested block (indirect local) // The inner lambda captures 'uniqueTimerKey' which is inside an 'if' block. // This should prevent hoisting to fieldReadOnly's body level because the // variable is not accessible at that block level - must stay inside the // outer lambda where it can see the captured variable. function clearTimer(_id) { } function fieldReadOnly(params = {}) { let {rawComponentName=null} = params if (true) { let uniqueTimerKey = rawComponentName return @() { onDetach = @() clearTimer(uniqueTimerKey) } } } ================================================ FILE: testData/ast/optimizations/closureHoisting/indirectLocalCapture.opt.txt ================================================ Block isRoot = 1, isBody = 0 |-VarDecl 'let', name = 'clearTimer', type = 'any' | Initializer | FunctionExpr name = 'clearTimer', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 1 | | Parameter #1 name = '_id', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 `-VarDecl 'let', name = 'fieldReadOnly', type = 'any' Initializer FunctionExpr name = 'fieldReadOnly', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 1 | Parameter #1 name = 'params', type = 'any' (has default) | DefaultValue | TableExpr `-Body Block isRoot = 0, isBody = 1 |-DestructuringDecl type = 'table' | |-Initializer | | Id 'params' | `-VarDecl 'let', name = 'rawComponentName', type = 'any' | Initializer | LiteralExpr LK_NULL `-IfStatement |-Condition | LiteralExpr LK_BOOL true `-ThenBranch Block isRoot = 0, isBody = 0 |-VarDecl 'let', name = 'uniqueTimerKey', type = 'any' | Initializer | Id 'rawComponentName' `-ReturnStatement FunctionExpr 'lambda' name = '(indirectLocalCapture.nut:16)', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 0 `-Body Block isRoot = 0, isBody = 1 |-VarDecl 'let', name = '$ch0', type = 'any' | Initializer | FunctionExpr 'lambda' name = '(indirectLocalCapture.nut:17)', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | CallExpr | |-Callee | | Id 'clearTimer' | `-Arguments | Argument #1 | Id 'uniqueTimerKey' `-ReturnStatement TableExpr Field |-Key | LiteralExpr LK_STRING "onDetach" `-Value Id '$ch0' ================================================ FILE: testData/ast/optimizations/closureHoisting/manyFuncs.nut ================================================ local z = 0 function foo(x, y) { let ar = [function fff(a, b) { let c = class { x = 10 function zed() { return 10 + this.x; } } return c().zed() }, function () { return 10 }, @(r) r * 2] ar[0](3, 4); } ================================================ FILE: testData/ast/optimizations/closureHoisting/manyFuncs.opt.txt ================================================ Block isRoot = 1, isBody = 0 |-VarDecl 'local', name = 'z', type = 'any' | Initializer | LiteralExpr LK_INT 0 |-VarDecl 'let', name = '$ch0', type = 'any' | Initializer | FunctionExpr name = 'fff', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 2 | | |-Parameter #1 name = 'a', type = 'any' | | `-Parameter #2 name = 'b', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | |-VarDecl 'let', name = 'c', type = 'any' | | Initializer | | ClassExpr | | |-Key: | | `-Members | | |-LiteralExpr LK_INT 10 | | `-FunctionExpr name = 'zed', pure = 0, nodiscard = 0, resultType = 'any' | | |-Parameters count = 0 | | `-Body | | Block isRoot = 0, isBody = 1 | | ReturnStatement | | BinExpr '+' | | |-Left | | | LiteralExpr LK_INT 10 | | `-Right | | GetFieldExpr '.' fieldName = 'x' | | Receiver | | Id 'this' | `-ReturnStatement | CallExpr | Callee | GetFieldExpr '.' fieldName = 'zed' | Receiver | CallExpr | Callee | Id 'c' |-VarDecl 'let', name = '$ch1', type = 'any' | Initializer | FunctionExpr name = '(manyFuncs.nut:12)', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | LiteralExpr LK_INT 10 |-VarDecl 'let', name = '$ch2', type = 'any' | Initializer | FunctionExpr 'lambda' name = '(manyFuncs.nut:12)', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 1 | | Parameter #1 name = 'r', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '*' | |-Left | | Id 'r' | `-Right | LiteralExpr LK_INT 2 `-VarDecl 'let', name = 'foo', type = 'any' Initializer FunctionExpr name = 'foo', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 2 | |-Parameter #1 name = 'x', type = 'any' | `-Parameter #2 name = 'y', type = 'any' `-Body Block isRoot = 0, isBody = 1 |-VarDecl 'let', name = 'ar', type = 'any' | Initializer | ArrayExpr | |-Element #1 | | Id '$ch0' | |-Element #2 | | Id '$ch1' | `-Element #3 | Id '$ch2' `-ExprStatement CallExpr |-Callee | GetSlotExpr '[' | |-Receiver | | Id 'ar' | `-Key | LiteralExpr LK_INT 0 `-Arguments |-Argument #1 | LiteralExpr LK_INT 3 `-Argument #2 LiteralExpr LK_INT 4 ================================================ FILE: testData/ast/optimizations/closureHoisting/multiDepthCapture.nut ================================================ // Test: closure captures from multiple depths // The lambda captures both 'rootVar' (depth 0) and 'paramVar' (depth 1) // It should hoist to depth 1 (outer's body), NOT to depth 0 (root) let rootVar = 1 function outer(paramVar) { function inner() { // This lambda captures from BOTH depth 0 (rootVar) and depth 1 (paramVar) // Must hoist to depth 1, not depth 0 return @() rootVar + paramVar } return inner() } ================================================ FILE: testData/ast/optimizations/closureHoisting/multiDepthCapture.opt.txt ================================================ Block isRoot = 1, isBody = 0 |-VarDecl 'let', name = 'rootVar', type = 'any' | Initializer | LiteralExpr LK_INT 1 `-VarDecl 'let', name = 'outer', type = 'any' Initializer FunctionExpr name = 'outer', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 1 | Parameter #1 name = 'paramVar', type = 'any' `-Body Block isRoot = 0, isBody = 1 |-VarDecl 'let', name = '$ch0', type = 'any' | Initializer | FunctionExpr 'lambda' name = '(multiDepthCapture.nut:11)', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '+' | |-Left | | Id 'rootVar' | `-Right | Id 'paramVar' |-VarDecl 'let', name = 'inner', type = 'any' | Initializer | FunctionExpr name = 'inner', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | Id '$ch0' `-ReturnStatement CallExpr Callee Id 'inner' ================================================ FILE: testData/ast/optimizations/closureHoisting/nestedParamShadow.nut ================================================ // Test: nested function parameter shadows a captured variable. // inner() captures 'x' from outer, but deep(x) has param 'x'. // inner should NOT be hoisted (it captures from immediate parent). function outer(x) { function inner() { function deep(x) { return x } return x } return inner() } ================================================ FILE: testData/ast/optimizations/closureHoisting/nestedParamShadow.opt.txt ================================================ Block isRoot = 1, isBody = 0 |-VarDecl 'let', name = '$ch0', type = 'any' | Initializer | FunctionExpr name = 'deep', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 1 | | Parameter #1 name = 'x', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | Id 'x' `-VarDecl 'let', name = 'outer', type = 'any' Initializer FunctionExpr name = 'outer', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 1 | Parameter #1 name = 'x', type = 'any' `-Body Block isRoot = 0, isBody = 1 |-VarDecl 'let', name = 'inner', type = 'any' | Initializer | FunctionExpr name = 'inner', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | |-VarDecl 'let', name = 'deep', type = 'any' | | Initializer | | Id '$ch0' | `-ReturnStatement | Id 'x' `-ReturnStatement CallExpr Callee Id 'inner' ================================================ FILE: testData/ast/optimizations/closureHoisting/paramDefaultCapture.nut ================================================ // Test 1: parameter default captures sibling from immediate parent scope. // 'bar' captures 'foo' from immediate parent -> blocks hoisting of bar. // 'foo' has no captures -> hoisted to root. function root() { function foo(...) { return "xyz" } function bar(source=foo) { return source } return bar() } // Test 2: parameter default captures from grandparent, body local shadows same name. // inner's default `a = x` captures outer's x. inner's body also declares `let x = 2`. // The body local must NOT shadow the outer capture during analysis. // inner should NOT be hoisted above outer. function outer() { let x = 1 function inner(a = x) { let x = 2 return a + x } return inner } ================================================ FILE: testData/ast/optimizations/closureHoisting/paramDefaultCapture.opt.txt ================================================ Block isRoot = 1, isBody = 0 |-VarDecl 'let', name = '$ch0', type = 'any' | Initializer | FunctionExpr name = 'foo', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 1 | | |-Parameter #1 name = 'vargv', type = 'any' (vararg) | | `-Vararg... | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | LiteralExpr LK_STRING "xyz" |-VarDecl 'let', name = 'root', type = 'any' | Initializer | FunctionExpr name = 'root', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | |-VarDecl 'let', name = 'foo', type = 'any' | | Initializer | | Id '$ch0' | |-VarDecl 'let', name = 'bar', type = 'any' | | Initializer | | FunctionExpr name = 'bar', pure = 0, nodiscard = 0, resultType = 'any' | | |-Parameters count = 1 | | | Parameter #1 name = 'source', type = 'any' (has default) | | | DefaultValue | | | Id 'foo' | | `-Body | | Block isRoot = 0, isBody = 1 | | ReturnStatement | | Id 'source' | `-ReturnStatement | CallExpr | Callee | Id 'bar' `-VarDecl 'let', name = 'outer', type = 'any' Initializer FunctionExpr name = 'outer', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 0 `-Body Block isRoot = 0, isBody = 1 |-VarDecl 'let', name = 'x', type = 'any' | Initializer | LiteralExpr LK_INT 1 |-VarDecl 'let', name = 'inner', type = 'any' | Initializer | FunctionExpr name = 'inner', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 1 | | Parameter #1 name = 'a', type = 'any' (has default) | | DefaultValue | | Id 'x' | `-Body | Block isRoot = 0, isBody = 1 | |-VarDecl 'let', name = 'x', type = 'any' | | Initializer | | LiteralExpr LK_INT 2 | `-ReturnStatement | BinExpr '+' | |-Left | | Id 'a' | `-Right | Id 'x' `-ReturnStatement Id 'inner' ================================================ FILE: testData/ast/optimizations/closureHoisting/recursion1.nut ================================================ function foo(x, y) { function bar(a, b) { function qux(c, d) { return c(d) } return qux(bar, b) } } ================================================ FILE: testData/ast/optimizations/closureHoisting/recursion1.opt.txt ================================================ Block isRoot = 1, isBody = 0 |-VarDecl 'let', name = '$ch0', type = 'any' | Initializer | FunctionExpr name = 'qux', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 2 | | |-Parameter #1 name = 'c', type = 'any' | | `-Parameter #2 name = 'd', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | CallExpr | |-Callee | | Id 'c' | `-Arguments | Argument #1 | Id 'd' `-VarDecl 'let', name = 'foo', type = 'any' Initializer FunctionExpr name = 'foo', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 2 | |-Parameter #1 name = 'x', type = 'any' | `-Parameter #2 name = 'y', type = 'any' `-Body Block isRoot = 0, isBody = 1 VarDecl 'let', name = 'bar', type = 'any' Initializer FunctionExpr name = 'bar', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 2 | |-Parameter #1 name = 'a', type = 'any' | `-Parameter #2 name = 'b', type = 'any' `-Body Block isRoot = 0, isBody = 1 |-VarDecl 'let', name = 'qux', type = 'any' | Initializer | Id '$ch0' `-ReturnStatement CallExpr |-Callee | Id 'qux' `-Arguments |-Argument #1 | Id 'bar' `-Argument #2 Id 'b' ================================================ FILE: testData/ast/optimizations/closureHoisting/simple.nut ================================================ function foo(x, y) { return function(a, b) { return a + b; } } ================================================ FILE: testData/ast/optimizations/closureHoisting/simple.opt.txt ================================================ Block isRoot = 1, isBody = 0 |-VarDecl 'let', name = '$ch0', type = 'any' | Initializer | FunctionExpr name = '(simple.nut:3)', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 2 | | |-Parameter #1 name = 'a', type = 'any' | | `-Parameter #2 name = 'b', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '+' | |-Left | | Id 'a' | `-Right | Id 'b' `-VarDecl 'let', name = 'foo', type = 'any' Initializer FunctionExpr name = 'foo', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 2 | |-Parameter #1 name = 'x', type = 'any' | `-Parameter #2 name = 'y', type = 'any' `-Body Block isRoot = 0, isBody = 1 ReturnStatement Id '$ch0' ================================================ FILE: testData/ast/optimizations/closureHoisting/simple2.nut ================================================ local z = 0 function foo(x, y) { let f1 = function(a) { let f2 = function(x) { let f3 = function(c) { return a + c } return x + y } } } ================================================ FILE: testData/ast/optimizations/closureHoisting/simple2.opt.txt ================================================ Block isRoot = 1, isBody = 0 |-VarDecl 'local', name = 'z', type = 'any' | Initializer | LiteralExpr LK_INT 0 `-VarDecl 'let', name = 'foo', type = 'any' Initializer FunctionExpr name = 'foo', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 2 | |-Parameter #1 name = 'x', type = 'any' | `-Parameter #2 name = 'y', type = 'any' `-Body Block isRoot = 0, isBody = 1 VarDecl 'let', name = 'f1', type = 'any' Initializer FunctionExpr name = 'f1', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 1 | Parameter #1 name = 'a', type = 'any' `-Body Block isRoot = 0, isBody = 1 |-VarDecl 'let', name = '$ch0', type = 'any' | Initializer | FunctionExpr name = 'f3', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 1 | | Parameter #1 name = 'c', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | BinExpr '+' | |-Left | | Id 'a' | `-Right | Id 'c' `-VarDecl 'let', name = 'f2', type = 'any' Initializer FunctionExpr name = 'f2', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 1 | Parameter #1 name = 'x', type = 'any' `-Body Block isRoot = 0, isBody = 1 |-VarDecl 'let', name = 'f3', type = 'any' | Initializer | Id '$ch0' `-ReturnStatement BinExpr '+' |-Left | Id 'x' `-Right Id 'y' ================================================ FILE: testData/ast/optimizations/closureHoisting/tooManyLocals.nut ================================================ let v001=0, v002=0, v003=0, v004=0, v005=0, v006=0, v007=0, v008=0, v009=0, v010=0 let v011=0, v012=0, v013=0, v014=0, v015=0, v016=0, v017=0, v018=0, v019=0, v020=0 let v021=0, v022=0, v023=0, v024=0, v025=0, v026=0, v027=0, v028=0, v029=0, v030=0 let v031=0, v032=0, v033=0, v034=0, v035=0, v036=0, v037=0, v038=0, v039=0, v040=0 let v041=0, v042=0, v043=0, v044=0, v045=0, v046=0, v047=0, v048=0, v049=0, v050=0 let v051=0, v052=0, v053=0, v054=0, v055=0, v056=0, v057=0, v058=0, v059=0, v060=0 let v061=0, v062=0, v063=0, v064=0, v065=0, v066=0, v067=0, v068=0, v069=0, v070=0 let v071=0, v072=0, v073=0, v074=0, v075=0, v076=0, v077=0, v078=0, v079=0, v080=0 let v081=0, v082=0, v083=0, v084=0, v085=0, v086=0, v087=0, v088=0, v089=0, v090=0 let v091=0, v092=0, v093=0, v094=0, v095=0, v096=0, v097=0, v098=0, v099=0, v100=0 let v101=0, v102=0, v103=0, v104=0, v105=0, v106=0, v107=0, v108=0, v109=0, v110=0 let v111=0, v112=0, v113=0, v114=0, v115=0, v116=0, v117=0, v118=0, v119=0, v120=0 let v121=0, v122=0, v123=0, v124=0, v125=0, v126=0, v127=0, v128=0, v129=0, v130=0 let v131=0, v132=0, v133=0, v134=0, v135=0, v136=0, v137=0, v138=0, v139=0, v140=0 let v141=0, v142=0, v143=0, v144=0, v145=0, v146=0, v147=0, v148=0, v149=0, v150=0 let v151=0, v152=0, v153=0, v154=0, v155=0, v156=0, v157=0, v158=0, v159=0, v160=0 let v161=0, v162=0, v163=0, v164=0, v165=0, v166=0, v167=0, v168=0, v169=0, v170=0 let v171=0, v172=0, v173=0, v174=0, v175=0, v176=0, v177=0, v178=0, v179=0, v180=0 let v181=0, v182=0, v183=0, v184=0, v185=0, v186=0, v187=0, v188=0, v189=0, v190=0 let v191=0, v192=0, v193=0, v194=0, v195=0 function outer() { function inner1() { return 0 } function inner2() { return 0 } function inner3() { return 0 } function inner4() { return 0 } // the following do not fit into max amount of locals and will not be hoisted function inner5() { return 0 } function inner6() { return 0 } function inner7() { return 0 } function inner8() { return 0 } function inner9() { return 0 } return inner9() } ================================================ FILE: testData/ast/optimizations/closureHoisting/tooManyLocals.opt.txt ================================================ Block isRoot = 1, isBody = 0 |-DeclGroup | |-VarDecl 'let', name = 'v001', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v002', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v003', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v004', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v005', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v006', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v007', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v008', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v009', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | `-VarDecl 'let', name = 'v010', type = 'any' | Initializer | LiteralExpr LK_INT 0 |-DeclGroup | |-VarDecl 'let', name = 'v011', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v012', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v013', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v014', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v015', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v016', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v017', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v018', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v019', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | `-VarDecl 'let', name = 'v020', type = 'any' | Initializer | LiteralExpr LK_INT 0 |-DeclGroup | |-VarDecl 'let', name = 'v021', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v022', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v023', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v024', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v025', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v026', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v027', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v028', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v029', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | `-VarDecl 'let', name = 'v030', type = 'any' | Initializer | LiteralExpr LK_INT 0 |-DeclGroup | |-VarDecl 'let', name = 'v031', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v032', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v033', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v034', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v035', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v036', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v037', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v038', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v039', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | `-VarDecl 'let', name = 'v040', type = 'any' | Initializer | LiteralExpr LK_INT 0 |-DeclGroup | |-VarDecl 'let', name = 'v041', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v042', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v043', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v044', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v045', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v046', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v047', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v048', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v049', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | `-VarDecl 'let', name = 'v050', type = 'any' | Initializer | LiteralExpr LK_INT 0 |-DeclGroup | |-VarDecl 'let', name = 'v051', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v052', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v053', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v054', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v055', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v056', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v057', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v058', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v059', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | `-VarDecl 'let', name = 'v060', type = 'any' | Initializer | LiteralExpr LK_INT 0 |-DeclGroup | |-VarDecl 'let', name = 'v061', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v062', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v063', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v064', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v065', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v066', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v067', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v068', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v069', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | `-VarDecl 'let', name = 'v070', type = 'any' | Initializer | LiteralExpr LK_INT 0 |-DeclGroup | |-VarDecl 'let', name = 'v071', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v072', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v073', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v074', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v075', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v076', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v077', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v078', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v079', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | `-VarDecl 'let', name = 'v080', type = 'any' | Initializer | LiteralExpr LK_INT 0 |-DeclGroup | |-VarDecl 'let', name = 'v081', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v082', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v083', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v084', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v085', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v086', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v087', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v088', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v089', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | `-VarDecl 'let', name = 'v090', type = 'any' | Initializer | LiteralExpr LK_INT 0 |-DeclGroup | |-VarDecl 'let', name = 'v091', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v092', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v093', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v094', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v095', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v096', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v097', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v098', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v099', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | `-VarDecl 'let', name = 'v100', type = 'any' | Initializer | LiteralExpr LK_INT 0 |-DeclGroup | |-VarDecl 'let', name = 'v101', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v102', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v103', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v104', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v105', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v106', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v107', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v108', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v109', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | `-VarDecl 'let', name = 'v110', type = 'any' | Initializer | LiteralExpr LK_INT 0 |-DeclGroup | |-VarDecl 'let', name = 'v111', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v112', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v113', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v114', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v115', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v116', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v117', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v118', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v119', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | `-VarDecl 'let', name = 'v120', type = 'any' | Initializer | LiteralExpr LK_INT 0 |-DeclGroup | |-VarDecl 'let', name = 'v121', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v122', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v123', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v124', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v125', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v126', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v127', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v128', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v129', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | `-VarDecl 'let', name = 'v130', type = 'any' | Initializer | LiteralExpr LK_INT 0 |-DeclGroup | |-VarDecl 'let', name = 'v131', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v132', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v133', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v134', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v135', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v136', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v137', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v138', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v139', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | `-VarDecl 'let', name = 'v140', type = 'any' | Initializer | LiteralExpr LK_INT 0 |-DeclGroup | |-VarDecl 'let', name = 'v141', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v142', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v143', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v144', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v145', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v146', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v147', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v148', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v149', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | `-VarDecl 'let', name = 'v150', type = 'any' | Initializer | LiteralExpr LK_INT 0 |-DeclGroup | |-VarDecl 'let', name = 'v151', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v152', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v153', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v154', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v155', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v156', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v157', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v158', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v159', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | `-VarDecl 'let', name = 'v160', type = 'any' | Initializer | LiteralExpr LK_INT 0 |-DeclGroup | |-VarDecl 'let', name = 'v161', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v162', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v163', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v164', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v165', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v166', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v167', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v168', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v169', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | `-VarDecl 'let', name = 'v170', type = 'any' | Initializer | LiteralExpr LK_INT 0 |-DeclGroup | |-VarDecl 'let', name = 'v171', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v172', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v173', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v174', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v175', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v176', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v177', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v178', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v179', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | `-VarDecl 'let', name = 'v180', type = 'any' | Initializer | LiteralExpr LK_INT 0 |-DeclGroup | |-VarDecl 'let', name = 'v181', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v182', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v183', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v184', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v185', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v186', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v187', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v188', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v189', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | `-VarDecl 'let', name = 'v190', type = 'any' | Initializer | LiteralExpr LK_INT 0 |-DeclGroup | |-VarDecl 'let', name = 'v191', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v192', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v193', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | |-VarDecl 'let', name = 'v194', type = 'any' | | Initializer | | LiteralExpr LK_INT 0 | `-VarDecl 'let', name = 'v195', type = 'any' | Initializer | LiteralExpr LK_INT 0 |-VarDecl 'let', name = '$ch0', type = 'any' | Initializer | FunctionExpr name = 'inner1', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | LiteralExpr LK_INT 0 |-VarDecl 'let', name = '$ch1', type = 'any' | Initializer | FunctionExpr name = 'inner2', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | LiteralExpr LK_INT 0 |-VarDecl 'let', name = '$ch2', type = 'any' | Initializer | FunctionExpr name = 'inner3', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | LiteralExpr LK_INT 0 |-VarDecl 'let', name = '$ch3', type = 'any' | Initializer | FunctionExpr name = 'inner4', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | LiteralExpr LK_INT 0 `-VarDecl 'let', name = 'outer', type = 'any' Initializer FunctionExpr name = 'outer', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 0 `-Body Block isRoot = 0, isBody = 1 |-VarDecl 'let', name = 'inner1', type = 'any' | Initializer | Id '$ch0' |-VarDecl 'let', name = 'inner2', type = 'any' | Initializer | Id '$ch1' |-VarDecl 'let', name = 'inner3', type = 'any' | Initializer | Id '$ch2' |-VarDecl 'let', name = 'inner4', type = 'any' | Initializer | Id '$ch3' |-VarDecl 'let', name = 'inner5', type = 'any' | Initializer | FunctionExpr name = 'inner5', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | LiteralExpr LK_INT 0 |-VarDecl 'let', name = 'inner6', type = 'any' | Initializer | FunctionExpr name = 'inner6', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | LiteralExpr LK_INT 0 |-VarDecl 'let', name = 'inner7', type = 'any' | Initializer | FunctionExpr name = 'inner7', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | LiteralExpr LK_INT 0 |-VarDecl 'let', name = 'inner8', type = 'any' | Initializer | FunctionExpr name = 'inner8', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | LiteralExpr LK_INT 0 |-VarDecl 'let', name = 'inner9', type = 'any' | Initializer | FunctionExpr name = 'inner9', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | LiteralExpr LK_INT 0 `-ReturnStatement CallExpr Callee Id 'inner9' ================================================ FILE: testData/ast/optimizations/closureHoisting/typedDefaultReturnType.nut ================================================ // Typed default parameter safety for closure hoisting: // Functions with typed defaults (param with both type annotation and default // value) are never hoisted. The type check at closure creation can throw, // and hoisting would move that throw to a different scope. function outer() { function inner() { // NOT hoisted: has typed default (type + default value) let typedDefault = function(x: int = 42) { return x } // Hoisted: has default but no type annotation let untypedDefault = function(x = 42) { return x } // Hoisted: has type annotation but no default let typedNoDefault = function(x: int) { return x } // Hoisted: no type, no default let plain = function(x) { return x } return typedDefault() + untypedDefault() + typedNoDefault(1) + plain(1) } return inner() } ================================================ FILE: testData/ast/optimizations/closureHoisting/typedDefaultReturnType.opt.txt ================================================ Block isRoot = 1, isBody = 0 |-VarDecl 'let', name = '$ch0', type = 'any' | Initializer | FunctionExpr name = 'untypedDefault', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 1 | | Parameter #1 name = 'x', type = 'any' (has default) | | DefaultValue | | LiteralExpr LK_INT 42 | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | Id 'x' |-VarDecl 'let', name = '$ch1', type = 'any' | Initializer | FunctionExpr name = 'typedNoDefault', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 1 | | Parameter #1 name = 'x', type = 'int' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | Id 'x' |-VarDecl 'let', name = '$ch2', type = 'any' | Initializer | FunctionExpr name = 'plain', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 1 | | Parameter #1 name = 'x', type = 'any' | `-Body | Block isRoot = 0, isBody = 1 | ReturnStatement | Id 'x' |-VarDecl 'let', name = '$ch3', type = 'any' | Initializer | FunctionExpr name = 'inner', pure = 0, nodiscard = 0, resultType = 'any' | |-Parameters count = 0 | `-Body | Block isRoot = 0, isBody = 1 | |-VarDecl 'let', name = 'typedDefault', type = 'any' | | Initializer | | FunctionExpr name = 'typedDefault', pure = 0, nodiscard = 0, resultType = 'any' | | |-Parameters count = 1 | | | Parameter #1 name = 'x', type = 'int' (has default) | | | DefaultValue | | | LiteralExpr LK_INT 42 | | `-Body | | Block isRoot = 0, isBody = 1 | | ReturnStatement | | Id 'x' | |-VarDecl 'let', name = 'untypedDefault', type = 'any' | | Initializer | | Id '$ch0' | |-VarDecl 'let', name = 'typedNoDefault', type = 'any' | | Initializer | | Id '$ch1' | |-VarDecl 'let', name = 'plain', type = 'any' | | Initializer | | Id '$ch2' | `-ReturnStatement | BinExpr '+' | |-Left | | BinExpr '+' | | |-Left | | | BinExpr '+' | | | |-Left | | | | CallExpr | | | | Callee | | | | Id 'typedDefault' | | | `-Right | | | CallExpr | | | Callee | | | Id 'untypedDefault' | | `-Right | | CallExpr | | |-Callee | | | Id 'typedNoDefault' | | `-Arguments | | Argument #1 | | LiteralExpr LK_INT 1 | `-Right | CallExpr | |-Callee | | Id 'plain' | `-Arguments | Argument #1 | LiteralExpr LK_INT 1 `-VarDecl 'let', name = 'outer', type = 'any' Initializer FunctionExpr name = 'outer', pure = 0, nodiscard = 0, resultType = 'any' |-Parameters count = 0 `-Body Block isRoot = 0, isBody = 1 |-VarDecl 'let', name = 'inner', type = 'any' | Initializer | Id '$ch3' `-ReturnStatement CallExpr Callee Id 'inner' ================================================ FILE: testData/diagnostics/1000_local_variables.diag.txt ================================================ ERROR: internal compiler error: too many locals testData/diagnostics/1000_local_variables.nut:-1:-1 ================================================ FILE: testData/diagnostics/1000_local_variables.nut ================================================ local a000, a001, a002, a003, a004, a005, a006, a007, a008, a009 local a010, a011, a012, a013, a014, a015, a016, a017, a018, a019 local a020, a021, a022, a023, a024, a025, a026, a027, a028, a029 local a030, a031, a032, a033, a034, a035, a036, a037, a038, a039 local a040, a041, a042, a043, a044, a045, a046, a047, a048, a049 local a050, a051, a052, a053, a054, a055, a056, a057, a058, a059 local a060, a061, a062, a063, a064, a065, a066, a067, a068, a069 local a070, a071, a072, a073, a074, a075, a076, a077, a078, a079 local a080, a081, a082, a083, a084, a085, a086, a087, a088, a089 local a090, a091, a092, a093, a094, a095, a096, a097, a098, a099 local a100, a101, a102, a103, a104, a105, a106, a107, a108, a109 local a110, a111, a112, a113, a114, a115, a116, a117, a118, a119 local a120, a121, a122, a123, a124, a125, a126, a127, a128, a129 local a130, a131, a132, a133, a134, a135, a136, a137, a138, a139 local a140, a141, a142, a143, a144, a145, a146, a147, a148, a149 local a150, a151, a152, a153, a154, a155, a156, a157, a158, a159 local a160, a161, a162, a163, a164, a165, a166, a167, a168, a169 local a170, a171, a172, a173, a174, a175, a176, a177, a178, a179 local a180, a181, a182, a183, a184, a185, a186, a187, a188, a189 local a190, a191, a192, a193, a194, a195, a196, a197, a198, a199 local a200, a201, a202, a203, a204, a205, a206, a207, a208, a209 local a210, a211, a212, a213, a214, a215, a216, a217, a218, a219 local a220, a221, a222, a223, a224, a225, a226, a227, a228, a229 local a230, a231, a232, a233, a234, a235, a236, a237, a238, a239 local a240, a241, a242, a243, a244, a245, a246, a247, a248, a249 local a250, a251, a252, a253, a254, a255, a256, a257, a258, a259 local a260, a261, a262, a263, a264, a265, a266, a267, a268, a269 local a270, a271, a272, a273, a274, a275, a276, a277, a278, a279 local a280, a281, a282, a283, a284, a285, a286, a287, a288, a289 local a290, a291, a292, a293, a294, a295, a296, a297, a298, a299 local a300, a301, a302, a303, a304, a305, a306, a307, a308, a309 local a310, a311, a312, a313, a314, a315, a316, a317, a318, a319 local a320, a321, a322, a323, a324, a325, a326, a327, a328, a329 local a330, a331, a332, a333, a334, a335, a336, a337, a338, a339 local a340, a341, a342, a343, a344, a345, a346, a347, a348, a349 local a350, a351, a352, a353, a354, a355, a356, a357, a358, a359 local a360, a361, a362, a363, a364, a365, a366, a367, a368, a369 local a370, a371, a372, a373, a374, a375, a376, a377, a378, a379 local a380, a381, a382, a383, a384, a385, a386, a387, a388, a389 local a390, a391, a392, a393, a394, a395, a396, a397, a398, a399 local a400, a401, a402, a403, a404, a405, a406, a407, a408, a409 local a410, a411, a412, a413, a414, a415, a416, a417, a418, a419 local a420, a421, a422, a423, a424, a425, a426, a427, a428, a429 local a430, a431, a432, a433, a434, a435, a436, a437, a438, a439 local a440, a441, a442, a443, a444, a445, a446, a447, a448, a449 local a450, a451, a452, a453, a454, a455, a456, a457, a458, a459 local a460, a461, a462, a463, a464, a465, a466, a467, a468, a469 local a470, a471, a472, a473, a474, a475, a476, a477, a478, a479 local a480, a481, a482, a483, a484, a485, a486, a487, a488, a489 local a490, a491, a492, a493, a494, a495, a496, a497, a498, a499 local a500, a501, a502, a503, a504, a505, a506, a507, a508, a509 local a510, a511, a512, a513, a514, a515, a516, a517, a518, a519 local a520, a521, a522, a523, a524, a525, a526, a527, a528, a529 local a530, a531, a532, a533, a534, a535, a536, a537, a538, a539 local a540, a541, a542, a543, a544, a545, a546, a547, a548, a549 local a550, a551, a552, a553, a554, a555, a556, a557, a558, a559 local a560, a561, a562, a563, a564, a565, a566, a567, a568, a569 local a570, a571, a572, a573, a574, a575, a576, a577, a578, a579 local a580, a581, a582, a583, a584, a585, a586, a587, a588, a589 local a590, a591, a592, a593, a594, a595, a596, a597, a598, a599 local a600, a601, a602, a603, a604, a605, a606, a607, a608, a609 local a610, a611, a612, a613, a614, a615, a616, a617, a618, a619 local a620, a621, a622, a623, a624, a625, a626, a627, a628, a629 local a630, a631, a632, a633, a634, a635, a636, a637, a638, a639 local a640, a641, a642, a643, a644, a645, a646, a647, a648, a649 local a650, a651, a652, a653, a654, a655, a656, a657, a658, a659 local a660, a661, a662, a663, a664, a665, a666, a667, a668, a669 local a670, a671, a672, a673, a674, a675, a676, a677, a678, a679 local a680, a681, a682, a683, a684, a685, a686, a687, a688, a689 local a690, a691, a692, a693, a694, a695, a696, a697, a698, a699 local a700, a701, a702, a703, a704, a705, a706, a707, a708, a709 local a710, a711, a712, a713, a714, a715, a716, a717, a718, a719 local a720, a721, a722, a723, a724, a725, a726, a727, a728, a729 local a730, a731, a732, a733, a734, a735, a736, a737, a738, a739 local a740, a741, a742, a743, a744, a745, a746, a747, a748, a749 local a750, a751, a752, a753, a754, a755, a756, a757, a758, a759 local a760, a761, a762, a763, a764, a765, a766, a767, a768, a769 local a770, a771, a772, a773, a774, a775, a776, a777, a778, a779 local a780, a781, a782, a783, a784, a785, a786, a787, a788, a789 local a790, a791, a792, a793, a794, a795, a796, a797, a798, a799 local a800, a801, a802, a803, a804, a805, a806, a807, a808, a809 local a810, a811, a812, a813, a814, a815, a816, a817, a818, a819 local a820, a821, a822, a823, a824, a825, a826, a827, a828, a829 local a830, a831, a832, a833, a834, a835, a836, a837, a838, a839 local a840, a841, a842, a843, a844, a845, a846, a847, a848, a849 local a850, a851, a852, a853, a854, a855, a856, a857, a858, a859 local a860, a861, a862, a863, a864, a865, a866, a867, a868, a869 local a870, a871, a872, a873, a874, a875, a876, a877, a878, a879 local a880, a881, a882, a883, a884, a885, a886, a887, a888, a889 local a890, a891, a892, a893, a894, a895, a896, a897, a898, a899 local a900, a901, a902, a903, a904, a905, a906, a907, a908, a909 local a910, a911, a912, a913, a914, a915, a916, a917, a918, a919 local a920, a921, a922, a923, a924, a925, a926, a927, a928, a929 local a930, a931, a932, a933, a934, a935, a936, a937, a938, a939 local a940, a941, a942, a943, a944, a945, a946, a947, a948, a949 local a950, a951, a952, a953, a954, a955, a956, a957, a958, a959 local a960, a961, a962, a963, a964, a965, a966, a967, a968, a969 local a970, a971, a972, a973, a974, a975, a976, a977, a978, a979 local a980, a981, a982, a983, a984, a985, a986, a987, a988, a989 local a990, a991, a992, a993, a994, a995, a996, a997, a998, a999 ================================================ FILE: testData/diagnostics/50k_access_member.diag.txt ================================================ ERROR: AST too big. Consider simplifying it testData/diagnostics/50k_access_member.nut:4:202 .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x ^ .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x ================================================ FILE: testData/diagnostics/50k_access_member.nut ================================================ x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x .x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x ================================================ FILE: testData/diagnostics/50k_curly_brackets.diag.txt ================================================ ERROR: AST too big. Consider simplifying it testData/diagnostics/50k_curly_brackets.nut:2:122 {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ ^ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ ================================================ FILE: testData/diagnostics/50k_curly_brackets.nut ================================================ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{ {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{  ================================================ FILE: testData/diagnostics/50k_function_calls.diag.txt ================================================ ERROR: AST too big. Consider simplifying it testData/diagnostics/50k_function_calls.nut:1:70 p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( ^ p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( ================================================ FILE: testData/diagnostics/50k_function_calls.nut ================================================ p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p( p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(p(  ================================================ FILE: testData/diagnostics/50k_lambdas.diag.txt ================================================ ERROR: AST too big. Consider simplifying it testData/diagnostics/50k_lambdas.nut:1:93 @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() ^ @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() ================================================ FILE: testData/diagnostics/50k_lambdas.nut ================================================ @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@() @()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()@()  ================================================ FILE: testData/diagnostics/50k_nested_tables.diag.txt ================================================ ERROR: AST too big. Consider simplifying it testData/diagnostics/50k_nested_tables.nut:1:101 x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ ^ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ ================================================ FILE: testData/diagnostics/50k_nested_tables.nut ================================================ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={ x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={x={  ================================================ FILE: testData/diagnostics/50k_not.diag.txt ================================================ ERROR: AST too big. Consider simplifying it testData/diagnostics/50k_not.nut:2:115 !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ^ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! ================================================ FILE: testData/diagnostics/50k_not.nut ================================================ !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!  ================================================ FILE: testData/diagnostics/50k_paren_brackets.diag.txt ================================================ ERROR: AST too big. Consider simplifying it testData/diagnostics/50k_paren_brackets.nut:1:35 ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ^ ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ================================================ FILE: testData/diagnostics/50k_paren_brackets.nut ================================================ ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( ((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((( (((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((((  ================================================ FILE: testData/diagnostics/50k_square_brackets.diag.txt ================================================ ERROR: AST too big. Consider simplifying it testData/diagnostics/50k_square_brackets.nut:1:35 [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ ^ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ ================================================ FILE: testData/diagnostics/50k_square_brackets.nut ================================================ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[ [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[  ================================================ FILE: testData/diagnostics/50r_binop.diag.txt ================================================ ERROR: AST too big. Consider simplifying it testData/diagnostics/50r_binop.nut:4:200 x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ ^ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ ================================================ FILE: testData/diagnostics/50r_binop.nut ================================================ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+ x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x+x ================================================ FILE: testData/diagnostics/assign_to_expr.diag.txt ================================================ ERROR: can't assign to expression testData/diagnostics/assign_to_expr.nut:2:0 local x x-- = 1 ^-- ================================================ FILE: testData/diagnostics/assign_to_expr.nut ================================================ local x x-- = 1 ================================================ FILE: testData/diagnostics/assign_to_expr2.diag.txt ================================================ ERROR: can't assign to expression testData/diagnostics/assign_to_expr2.nut:1:13 local x; @() !x = 1 ^- ================================================ FILE: testData/diagnostics/assign_to_expr2.nut ================================================ local x; @() !x = 1 ================================================ FILE: testData/diagnostics/assign_to_optional_7.diag.txt ================================================ ERROR: can't assign to expression testData/diagnostics/assign_to_optional_7.nut:2:0 local v = {v = {v = 0}} v.v ?? v = 3 ^------- ================================================ FILE: testData/diagnostics/assign_to_optional_7.nut ================================================ local v = {v = {v = 0}} v.v ?? v = 3 ================================================ FILE: testData/diagnostics/base_as_array_assign.diag.txt ================================================ ERROR: can't assign to expression testData/diagnostics/base_as_array_assign.nut:1:0 base["x"] = 1 ^-------- ================================================ FILE: testData/diagnostics/base_as_array_assign.nut ================================================ base["x"] = 1 ================================================ FILE: testData/diagnostics/base_as_array_pp.diag.txt ================================================ ERROR: argument of inc/dec operation is not assignable testData/diagnostics/base_as_array_pp.nut:1:0 base[0]++ ^------ ================================================ FILE: testData/diagnostics/base_as_array_pp.nut ================================================ base[0]++ ================================================ FILE: testData/diagnostics/base_pp.diag.txt ================================================ ERROR: argument of inc/dec operation is not assignable testData/diagnostics/base_pp.nut:1:0 base++ ^--- ================================================ FILE: testData/diagnostics/base_pp.nut ================================================ base++ ================================================ FILE: testData/diagnostics/base_x_pp.diag.txt ================================================ ERROR: argument of inc/dec operation is not assignable testData/diagnostics/base_x_pp.nut:1:0 base.x++ ^----- ================================================ FILE: testData/diagnostics/base_x_pp.nut ================================================ base.x++ ================================================ FILE: testData/diagnostics/binding_assign.diag.txt ================================================ ERROR: can't assign to binding 'a' (probably declaring using 'local' was intended, but 'let' was used) testData/diagnostics/binding_assign.nut:2:0 let a = 2 a = 3 ^ ================================================ FILE: testData/diagnostics/binding_assign.nut ================================================ let a = 2 a = 3 ================================================ FILE: testData/diagnostics/clone_op_allowed.diag.txt ================================================ ERROR: expected 'IDENTIFIER' testData/diagnostics/clone_op_allowed.nut.txt:10:12 let ct0 = clone t let ct1 = t.clone() ^---- ================================================ FILE: testData/diagnostics/clone_op_allowed.nut.txt ================================================ #allow-clone-operator let t = { a = 10, b = 20 } let ct0 = clone t let ct1 = t.clone() ================================================ FILE: testData/diagnostics/clone_op_forbiden.diag.txt ================================================ ERROR: end of statement expected (; or lf) testData/diagnostics/clone_op_forbiden.nut.txt:10:16 let ct1 = t.clone() let ct0 = clone t ^ ================================================ FILE: testData/diagnostics/clone_op_forbiden.nut.txt ================================================ #forbid-clone-operator let t = { a = 10, b = 20 } let ct1 = t.clone() let ct0 = clone t ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/empty_literal.diag.txt ================================================ ERROR: empty constant testData/diagnostics/compilation_errors/lex_errors/empty_literal.nut:2:8 // EXPECT_ERROR: "empty constant" let x = '' ^- ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/empty_literal.nut ================================================ // EXPECT_ERROR: "empty constant" let x = '' ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/fp_exp_expected.diag.txt ================================================ ERROR: exponent expected testData/diagnostics/compilation_errors/lex_errors/fp_exp_expected.nut:2:8 // EXPECT_ERROR: "exponent expected" let x = 1.0e ^--- ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/fp_exp_expected.nut ================================================ // EXPECT_ERROR: "exponent expected" let x = 1.0e ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/hex_digits_expected.diag.txt ================================================ ERROR: expected hex digits after '0x' testData/diagnostics/compilation_errors/lex_errors/hex_digits_expected.nut:2:8 // EXPECT_ERROR: "expected hex digits after '0x'" let x = 0xGG ^- ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/hex_digits_expected.nut ================================================ // EXPECT_ERROR: "expected hex digits after '0x'" let x = 0xGG ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/hex_numbers_expected.diag.txt ================================================ ERROR: hexadecimal number expected testData/diagnostics/compilation_errors/lex_errors/hex_numbers_expected.nut:2:8 // EXPECT_ERROR: "hexadecimal number expected" let x = "\xZZ" ^-- ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/hex_numbers_expected.nut ================================================ // EXPECT_ERROR: "hexadecimal number expected" let x = "\xZZ" ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/hex_too_many_digits.diag.txt ================================================ ERROR: too many digits for a hex number testData/diagnostics/compilation_errors/lex_errors/hex_too_many_digits.nut:2:8 // EXPECT_ERROR: "too many digits for a hex number" let x = 0x100000000000000000 ^------------------- ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/hex_too_many_digits.nut ================================================ // EXPECT_ERROR: "too many digits for a hex number" let x = 0x100000000000000000 ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/invalid_token.diag.txt ================================================ ERROR: invalid token '..' testData/diagnostics/compilation_errors/lex_errors/invalid_token.nut:2:0 // EXPECT_ERROR: "invalid token" .. ^- ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/invalid_token.nut ================================================ // EXPECT_ERROR: "invalid token" .. ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/literal_overflow.diag.txt ================================================ ERROR: integer constant overflow testData/diagnostics/compilation_errors/lex_errors/literal_overflow.nut:2:8 // EXPECT_ERROR: "constant overflow" let x = 99999999999999999999999999999999999999999999 ^------------------------------------------- ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/literal_overflow.nut ================================================ // EXPECT_ERROR: "constant overflow" let x = 99999999999999999999999999999999999999999999 ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/literal_underflow.diag.txt ================================================ ERROR: float constant underflow testData/diagnostics/compilation_errors/lex_errors/literal_underflow.nut:2:8 // EXPECT_ERROR: "constant underflow" let x = 1e-999999 ^-------- ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/literal_underflow.nut ================================================ // EXPECT_ERROR: "constant underflow" let x = 1e-999999 ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/malformed_number.diag.txt ================================================ ERROR: malformed number testData/diagnostics/compilation_errors/lex_errors/malformed_number.nut:2:8 // EXPECT_ERROR: "malformed number" let x = 1.2.3 ^-- ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/malformed_number.nut ================================================ // EXPECT_ERROR: "malformed number" let x = 1.2.3 ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/newline_in_const.diag.txt ================================================ ERROR: newline in a constant testData/diagnostics/compilation_errors/lex_errors/newline_in_const.nut:2:8 // EXPECT_ERROR: "newline in a constant" let x = "hello ^------ " ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/newline_in_const.nut ================================================ // EXPECT_ERROR: "newline in a constant" let x = "hello " ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/octal_not_supported.diag.txt ================================================ ERROR: leading 0 is not allowed, octal numbers are not supported testData/diagnostics/compilation_errors/lex_errors/octal_not_supported.nut:2:8 // EXPECT_ERROR: "octal numbers are not supported" let x = 0755 ^ ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/octal_not_supported.nut ================================================ // EXPECT_ERROR: "octal numbers are not supported" let x = 0755 ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/trailing_block_comment.diag.txt ================================================ ERROR: missing "*/" in comment testData/diagnostics/compilation_errors/lex_errors/trailing_block_comment.nut:2:0 // EXPECT_ERROR: "in comment" /* this comment is never closed ^-------------------------------------------- let x = 42 ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/trailing_block_comment.nut ================================================ // EXPECT_ERROR: "in comment" /* this comment is never closed let x = 42 ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/unexpected_char.diag.txt ================================================ ERROR: unexpected character '(control)' testData/diagnostics/compilation_errors/lex_errors/unexpected_char.nut:2:8 // EXPECT_ERROR: "unexpected character" let x =  ^ ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/unexpected_char.nut ================================================ // EXPECT_ERROR: "unexpected character" let x =  ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/unfinished_string.diag.txt ================================================ ERROR: unfinished string testData/diagnostics/compilation_errors/lex_errors/unfinished_string.nut:2:8 // EXPECT_ERROR: "unfinished string" let x = "hello ^----- ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/unfinished_string.nut ================================================ // EXPECT_ERROR: "unfinished string" let x = "hello ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/unrecognised_escape.diag.txt ================================================ ERROR: unrecognised escape char testData/diagnostics/compilation_errors/lex_errors/unrecognised_escape.nut:2:8 // EXPECT_ERROR: "unrecognised escape char" let x = "\q" ^- ================================================ FILE: testData/diagnostics/compilation_errors/lex_errors/unrecognised_escape.nut ================================================ // EXPECT_ERROR: "unrecognised escape char" let x = "\q" ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/assign_to_binding.diag.txt ================================================ ERROR: can't assign to binding 'x' (probably declaring using 'local' was intended, but 'let' was used) testData/diagnostics/compilation_errors/sema_errors/assign_to_binding.nut:3:0 let x = 10 x = 20 ^ ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/assign_to_binding.nut ================================================ // EXPECT_ERROR: "can't assign to binding" let x = 10 x = 20 ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/assign_to_expr.diag.txt ================================================ ERROR: can't assign to expression testData/diagnostics/compilation_errors/sema_errors/assign_to_expr.nut:2:0 // EXPECT_ERROR: "can't assign to expression" (1 + 2) = 5 ^----- ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/assign_to_expr.nut ================================================ // EXPECT_ERROR: "can't assign to expression" (1 + 2) = 5 ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/cannot_delete.diag.txt ================================================ ERROR: can't delete an (outer) local testData/diagnostics/compilation_errors/sema_errors/cannot_delete.nut:4:0 local x = 5 delete x ^------- ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/cannot_delete.nut ================================================ // EXPECT_ERROR: "can't delete" #allow-delete-operator local x = 5 delete x ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/conflicts_with.diag.txt ================================================ ERROR: Local variable name 'foo' conflicts with existing local variable testData/diagnostics/compilation_errors/sema_errors/conflicts_with.nut:3:0 function foo() { return 1 } local foo = 2 ^------------ ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/conflicts_with.nut ================================================ // EXPECT_ERROR: "conflicts with" function foo() { return 1 } local foo = 2 ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/constant_field_not_found.diag.txt ================================================ ERROR: Field 'c' not found in constant object testData/diagnostics/compilation_errors/sema_errors/constant_field_not_found.nut:3:10 const TBL = {a = 1, b = 2} const X = TBL.c ^---- ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/constant_field_not_found.nut ================================================ // EXPECT_ERROR: "not found in constant object" const TBL = {a = 1, b = 2} const X = TBL.c ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/constant_slot_not_found.diag.txt ================================================ ERROR: string slot c not found in constant object testData/diagnostics/compilation_errors/sema_errors/constant_slot_not_found.nut:3:14 const TBL = {a = 1, b = 2} const X = TBL["c"] ^-- ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/constant_slot_not_found.nut ================================================ // EXPECT_ERROR: "not found in constant object" const TBL = {a = 1, b = 2} const X = TBL["c"] ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/duplicate_func_attr.diag.txt ================================================ ERROR: duplicate attribute 'pure' testData/diagnostics/compilation_errors/sema_errors/duplicate_func_attr.nut:2:16 // EXPECT_ERROR: "duplicate attribute" let f = @[pure, pure](x) x + 1 ^--- ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/duplicate_func_attr.nut ================================================ // EXPECT_ERROR: "duplicate attribute" let f = @[pure, pure](x) x + 1 ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/duplicate_key.diag.txt ================================================ ERROR: duplicate key 'a' testData/diagnostics/compilation_errors/sema_errors/duplicate_key.nut:4:4 a = 1, a = 2 ^ } ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/duplicate_key.nut ================================================ // EXPECT_ERROR: "duplicate key" let t = { a = 1, a = 2 } ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/id_is_not_const.diag.txt ================================================ ERROR: Identifier 'y' is not a compile-time constant testData/diagnostics/compilation_errors/sema_errors/id_is_not_const.nut:3:12 local y = 5 const BAD = y ^ ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/id_is_not_const.nut ================================================ // EXPECT_ERROR: "is not a compile-time constant" local y = 5 const BAD = y ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/inc_dec_not_assignable.diag.txt ================================================ ERROR: argument of inc/dec operation is not assignable testData/diagnostics/compilation_errors/sema_errors/inc_dec_not_assignable.nut:2:0 // EXPECT_ERROR: "not assignable" (1 + 2)++ ^----- ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/inc_dec_not_assignable.nut ================================================ // EXPECT_ERROR: "not assignable" (1 + 2)++ ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/initialization_required.diag.txt ================================================ ERROR: Initialization required testData/diagnostics/compilation_errors/sema_errors/initialization_required.nut:2:11 // EXPECT_ERROR: "Initialization required" if (local x; x < 10; x++) {} ^ ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/initialization_required.nut ================================================ // EXPECT_ERROR: "Initialization required" if (local x; x < 10; x++) {} ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/invalid_enum.diag.txt ================================================ ERROR: Field 'YELLOW' not found in constant object testData/diagnostics/compilation_errors/sema_errors/invalid_enum.nut:3:8 enum Color { RED, GREEN, BLUE } let x = Color.YELLOW ^----------- ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/invalid_enum.nut ================================================ // EXPECT_ERROR: "not found in constant" enum Color { RED, GREEN, BLUE } let x = Color.YELLOW ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/local_slot_create.diag.txt ================================================ ERROR: can't 'create' a local slot testData/diagnostics/compilation_errors/sema_errors/local_slot_create.nut:3:0 local x = 5 x <- 10 ^ ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/local_slot_create.nut ================================================ // EXPECT_ERROR: "can't 'create' a local slot" local x = 5 x <- 10 ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/loop_controller_not_in_loop_break.diag.txt ================================================ ERROR: 'break' has to be in a loop block testData/diagnostics/compilation_errors/sema_errors/loop_controller_not_in_loop_break.nut:3:4 function foo() { break ^---- } ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/loop_controller_not_in_loop_break.nut ================================================ // EXPECT_ERROR: "has to be in a loop block" function foo() { break } ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/loop_controller_not_in_loop_continue.diag.txt ================================================ ERROR: 'continue' has to be in a loop block testData/diagnostics/compilation_errors/sema_errors/loop_controller_not_in_loop_continue.nut:3:4 function foo() { continue ^------- } ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/loop_controller_not_in_loop_continue.nut ================================================ // EXPECT_ERROR: "has to be in a loop block" function foo() { continue } ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/not_allowed_in_const.diag.txt ================================================ ERROR: increment expression is not allowed in constant testData/diagnostics/compilation_errors/sema_errors/not_allowed_in_const.nut:3:10 local i = 0 const X = i++ ^-- ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/not_allowed_in_const.nut ================================================ // EXPECT_ERROR: "is not allowed in constant" local i = 0 const X = i++ ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/only_single_variable_declaration.diag.txt ================================================ ERROR: Only single variable declaration is allowed here testData/diagnostics/compilation_errors/sema_errors/only_single_variable_declaration.nut:2:15 // EXPECT_ERROR: "Only single variable declaration is allowed here" if (local a = 1, b = 2) {} ^ ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/only_single_variable_declaration.nut ================================================ // EXPECT_ERROR: "Only single variable declaration is allowed here" if (local a = 1, b = 2) {} ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/same_foreach_kv_names.diag.txt ================================================ ERROR: foreach() key and value names are the same: 'x' testData/diagnostics/compilation_errors/sema_errors/same_foreach_kv_names.nut:3:12 let arr = [1, 2, 3] foreach (x, x in arr) { ^ print(x) ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/same_foreach_kv_names.nut ================================================ // EXPECT_ERROR: "foreach() key and value names are the same" let arr = [1, 2, 3] foreach (x, x in arr) { print(x) } ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/space_sep_field_name.diag.txt ================================================ ERROR: Separate access operator '.' with its following name is forbidden testData/diagnostics/compilation_errors/sema_errors/space_sep_field_name.nut:3:11 let t = {a = 1} let x = t. a ^ ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/space_sep_field_name.nut ================================================ // EXPECT_ERROR: "Separate access operator" let t = {a = 1} let x = t. a ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/too_many_locals.diag.txt ================================================ ERROR: internal compiler error: too many locals testData/diagnostics/compilation_errors/sema_errors/too_many_locals.nut:-1:-1 ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/too_many_locals.nut ================================================ // EXPECT_ERROR: "too many" function too_many() { local a000=0;local a001=0;local a002=0;local a003=0;local a004=0;local a005=0;local a006=0;local a007=0;local a008=0;local a009=0 local a010=0;local a011=0;local a012=0;local a013=0;local a014=0;local a015=0;local a016=0;local a017=0;local a018=0;local a019=0 local a020=0;local a021=0;local a022=0;local a023=0;local a024=0;local a025=0;local a026=0;local a027=0;local a028=0;local a029=0 local a030=0;local a031=0;local a032=0;local a033=0;local a034=0;local a035=0;local a036=0;local a037=0;local a038=0;local a039=0 local a040=0;local a041=0;local a042=0;local a043=0;local a044=0;local a045=0;local a046=0;local a047=0;local a048=0;local a049=0 local a050=0;local a051=0;local a052=0;local a053=0;local a054=0;local a055=0;local a056=0;local a057=0;local a058=0;local a059=0 local a060=0;local a061=0;local a062=0;local a063=0;local a064=0;local a065=0;local a066=0;local a067=0;local a068=0;local a069=0 local a070=0;local a071=0;local a072=0;local a073=0;local a074=0;local a075=0;local a076=0;local a077=0;local a078=0;local a079=0 local a080=0;local a081=0;local a082=0;local a083=0;local a084=0;local a085=0;local a086=0;local a087=0;local a088=0;local a089=0 local a090=0;local a091=0;local a092=0;local a093=0;local a094=0;local a095=0;local a096=0;local a097=0;local a098=0;local a099=0 local a100=0;local a101=0;local a102=0;local a103=0;local a104=0;local a105=0;local a106=0;local a107=0;local a108=0;local a109=0 local a110=0;local a111=0;local a112=0;local a113=0;local a114=0;local a115=0;local a116=0;local a117=0;local a118=0;local a119=0 local a120=0;local a121=0;local a122=0;local a123=0;local a124=0;local a125=0;local a126=0;local a127=0;local a128=0;local a129=0 local a130=0;local a131=0;local a132=0;local a133=0;local a134=0;local a135=0;local a136=0;local a137=0;local a138=0;local a139=0 local a140=0;local a141=0;local a142=0;local a143=0;local a144=0;local a145=0;local a146=0;local a147=0;local a148=0;local a149=0 local a150=0;local a151=0;local a152=0;local a153=0;local a154=0;local a155=0;local a156=0;local a157=0;local a158=0;local a159=0 local a160=0;local a161=0;local a162=0;local a163=0;local a164=0;local a165=0;local a166=0;local a167=0;local a168=0;local a169=0 local a170=0;local a171=0;local a172=0;local a173=0;local a174=0;local a175=0;local a176=0;local a177=0;local a178=0;local a179=0 local a180=0;local a181=0;local a182=0;local a183=0;local a184=0;local a185=0;local a186=0;local a187=0;local a188=0;local a189=0 local a190=0;local a191=0;local a192=0;local a193=0;local a194=0;local a195=0;local a196=0;local a197=0;local a198=0;local a199=0 local a200=0;local a201=0;local a202=0;local a203=0;local a204=0;local a205=0;local a206=0;local a207=0;local a208=0;local a209=0 local a210=0;local a211=0;local a212=0;local a213=0;local a214=0;local a215=0;local a216=0;local a217=0;local a218=0;local a219=0 local a220=0;local a221=0;local a222=0;local a223=0;local a224=0;local a225=0;local a226=0;local a227=0;local a228=0;local a229=0 local a230=0;local a231=0;local a232=0;local a233=0;local a234=0;local a235=0;local a236=0;local a237=0;local a238=0;local a239=0 local a240=0;local a241=0;local a242=0;local a243=0;local a244=0;local a245=0;local a246=0;local a247=0;local a248=0;local a249=0 local a250=0;local a251=0;local a252=0;local a253=0;local a254=0;local a255=0;local a256=0;local a257=0;local a258=0;local a259=0 return 0 } ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/type_differs.diag.txt ================================================ ERROR: expression of type 'string' cannot be assigned to type 'int' testData/diagnostics/compilation_errors/sema_errors/type_differs.nut:2:0 // EXPECT_ERROR: "differs from the declared type" local x: int = "not a number" ^---------------------------- ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/type_differs.nut ================================================ // EXPECT_ERROR: "differs from the declared type" local x: int = "not a number" ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/uninitialized_binding.diag.txt ================================================ ERROR: Binding 'x' must be initialized testData/diagnostics/compilation_errors/sema_errors/uninitialized_binding.nut:2:4 // EXPECT_ERROR: "must be initialized" let x ^ ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/uninitialized_binding.nut ================================================ // EXPECT_ERROR: "must be initialized" let x ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/unknown_symbol.diag.txt ================================================ ERROR: Unknown variable [undefined_var] testData/diagnostics/compilation_errors/sema_errors/unknown_symbol.nut:2:8 // EXPECT_ERROR: "Unknown variable" let x = undefined_var ^------------ ================================================ FILE: testData/diagnostics/compilation_errors/sema_errors/unknown_symbol.nut ================================================ // EXPECT_ERROR: "Unknown variable" let x = undefined_var ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/assign_inside_forbidden.diag.txt ================================================ ERROR: '=' inside 'if' is forbidden testData/diagnostics/compilation_errors/syntax_errors/assign_inside_forbidden.nut:3:10 local x = 5 if (x = 10) { ^ print("oops") ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/assign_inside_forbidden.nut ================================================ // EXPECT_ERROR: "is forbidden" local x = 5 if (x = 10) { print("oops") } ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/broken_slot_declaration.diag.txt ================================================ ERROR: cannot break deref/or comma needed after [exp]=exp slot declaration testData/diagnostics/compilation_errors/syntax_errors/broken_slot_declaration.nut:4:0 t [0] = 1 ^ ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/broken_slot_declaration.nut ================================================ // EXPECT_ERROR: "cannot break deref" local t = {} t [0] = 1 ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/compiler_internals_forbidden.diag.txt ================================================ ERROR: Access to compiler internals is forbidden testData/diagnostics/compilation_errors/syntax_errors/compiler_internals_forbidden.nut:2:8 // EXPECT_ERROR: "Access to compiler internals is forbidden" let x = $${return 42} ^-- ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/compiler_internals_forbidden.nut ================================================ // EXPECT_ERROR: "Access to compiler internals is forbidden" let x = $${return 42} ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/delete_op_forbidden.diag.txt ================================================ ERROR: Usage of 'delete' operator is forbidden. Use 'o.rawdelete("key")' instead testData/diagnostics/compilation_errors/syntax_errors/delete_op_forbidden.nut:4:0 let t = {a = 1} delete t.a ^----- ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/delete_op_forbidden.nut ================================================ // EXPECT_ERROR: "Usage of 'delete' operator is forbidden" #forbid-delete-operator let t = {a = 1} delete t.a ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/end_of_stmt_expected.diag.txt ================================================ ERROR: end of statement expected (; or lf) testData/diagnostics/compilation_errors/syntax_errors/end_of_stmt_expected.nut:2:10 // EXPECT_ERROR: "end of statement expected" let x = 5 let y = 6 ^-- ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/end_of_stmt_expected.nut ================================================ // EXPECT_ERROR: "end of statement expected" let x = 5 let y = 6 ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/expected_bracket.diag.txt ================================================ ERROR: expected ']' testData/diagnostics/compilation_errors/syntax_errors/expected_bracket.nut:2:31 // EXPECT_ERROR: "expected ']'" let a = [1, 2, 3]; let x = a[0 ^ ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/expected_bracket.nut ================================================ // EXPECT_ERROR: "expected ']'" let a = [1, 2, 3]; let x = a[0 ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/expected_colnum.diag.txt ================================================ ERROR: expected column number after #pos:: testData/diagnostics/compilation_errors/syntax_errors/expected_colnum.nut:2:0 // EXPECT_ERROR: "expected column number after #pos:" #pos:1:abc ^ let x = 1 ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/expected_colnum.nut ================================================ // EXPECT_ERROR: "expected column number after #pos:" #pos:1:abc let x = 1 ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/expected_expression.diag.txt ================================================ ERROR: expected 'expression' testData/diagnostics/compilation_errors/syntax_errors/expected_expression.nut:2:8 // EXPECT_ERROR: "expected 'expression'" let x = ^ ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/expected_expression.nut ================================================ // EXPECT_ERROR: "expected 'expression'" let x = ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/expected_identifier.diag.txt ================================================ ERROR: expected 'IDENTIFIER' testData/diagnostics/compilation_errors/syntax_errors/expected_identifier.nut:2:9 // EXPECT_ERROR: "expected 'IDENTIFIER'" function 123() { ^-- return 42 ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/expected_identifier.nut ================================================ // EXPECT_ERROR: "expected 'IDENTIFIER'" function 123() { return 42 } ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/expected_linenum.diag.txt ================================================ ERROR: expected line number after #pos: testData/diagnostics/compilation_errors/syntax_errors/expected_linenum.nut:2:0 // EXPECT_ERROR: "expected line number after #pos:" #pos:abc ^------- let x = 1 ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/expected_linenum.nut ================================================ // EXPECT_ERROR: "expected line number after #pos:" #pos:abc let x = 1 ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/expected_token_brace.diag.txt ================================================ ERROR: expected '}' testData/diagnostics/compilation_errors/syntax_errors/expected_token_brace.nut:2:29 // EXPECT_ERROR: "expected '}'" local {a, b = {x = 1, y = 2} ^ ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/expected_token_brace.nut ================================================ // EXPECT_ERROR: "expected '}'" local {a, b = {x = 1, y = 2} ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/expected_token_paren.diag.txt ================================================ ERROR: expected ')' testData/diagnostics/compilation_errors/syntax_errors/expected_token_paren.nut:2:9 // EXPECT_ERROR: "expected ')'" if (true { ^ ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/expected_token_paren.nut ================================================ // EXPECT_ERROR: "expected ')'" if (true { ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/expected_while.diag.txt ================================================ ERROR: expected 'while' testData/diagnostics/compilation_errors/syntax_errors/expected_while.nut:4:2 local x = 42 } ^ ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/expected_while.nut ================================================ // EXPECT_ERROR: "expected 'while'" do { local x = 42 } ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/global_consts_only.diag.txt ================================================ ERROR: global can be applied to const and enum only testData/diagnostics/compilation_errors/syntax_errors/global_consts_only.nut:2:7 // EXPECT_ERROR: "global can be applied to const and enum only" global local x = 5 ^---- ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/global_consts_only.nut ================================================ // EXPECT_ERROR: "global can be applied to const and enum only" global local x = 5 ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/invalid_type_name.diag.txt ================================================ ERROR: Invalid type name 'UnknownType' testData/diagnostics/compilation_errors/syntax_errors/invalid_type_name.nut:2:16 // EXPECT_ERROR: "Invalid type name" function foo(x: UnknownType) { ^---------- return x ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/invalid_type_name.nut ================================================ // EXPECT_ERROR: "Invalid type name" function foo(x: UnknownType) { return x } ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/invalid_type_name_suggestion.diag.txt ================================================ ERROR: Invalid type name 'Int', did you mean 'int'? testData/diagnostics/compilation_errors/syntax_errors/invalid_type_name_suggestion.nut:2:9 // EXPECT_ERROR: "did you mean" local x: Int = 5 ^-- ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/invalid_type_name_suggestion.nut ================================================ // EXPECT_ERROR: "did you mean" local x: Int = 5 ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/multiple_docstrings.diag.txt ================================================ ERROR: multiple docstrings in a single block testData/diagnostics/compilation_errors/syntax_errors/multiple_docstrings.nut:3:0 @@"first docstring" @@"second docstring" ^------------------- function foo() { ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/multiple_docstrings.nut ================================================ // EXPECT_ERROR: "multiple docstrings" @@"first docstring" @@"second docstring" function foo() { return 42 } ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/root_table_forbidden.diag.txt ================================================ ERROR: Access to root table is forbidden testData/diagnostics/compilation_errors/syntax_errors/root_table_forbidden.nut:3:8 #forbid-root-table let x = ::some_global ^- ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/root_table_forbidden.nut ================================================ // EXPECT_ERROR: "Access to root table is forbidden" #forbid-root-table let x = ::some_global ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/scalar_expected.diag.txt ================================================ ERROR: scalar expected : integer, float, or string testData/diagnostics/compilation_errors/syntax_errors/scalar_expected.nut:3:8 enum BadEnum { A = [1, 2, 3] ^ } ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/scalar_expected.nut ================================================ // EXPECT_ERROR: "scalar expected" enum BadEnum { A = [1, 2, 3] } ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/unsupported_directive.diag.txt ================================================ ERROR: unsupported directive 'this-directive-does-not-exist' testData/diagnostics/compilation_errors/syntax_errors/unsupported_directive.nut:2:0 // EXPECT_ERROR: "unsupported directive" #this-directive-does-not-exist ^----------------------------- let x = 42 ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/unsupported_directive.nut ================================================ // EXPECT_ERROR: "unsupported directive" #this-directive-does-not-exist let x = 42 ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/vararg_with_default.diag.txt ================================================ ERROR: function with default parameters cannot have variable number of parameters testData/diagnostics/compilation_errors/syntax_errors/vararg_with_default.nut:2:20 // EXPECT_ERROR: "function with default parameters cannot have variable number of parameters" function foo(a = 1, ...) { ^-- return a ================================================ FILE: testData/diagnostics/compilation_errors/syntax_errors/vararg_with_default.nut ================================================ // EXPECT_ERROR: "function with default parameters cannot have variable number of parameters" function foo(a = 1, ...) { return a } ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_arithmetic_mismatch.diag.txt ================================================ ERROR: expression of type 'int' cannot be assigned to type 'string' testData/diagnostics/compilation_errors/type_inference/test_arithmetic_mismatch.nut:4:0 local b: int = 20 local c: string = a + b ^---------------------- return c ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_arithmetic_mismatch.nut ================================================ // EXPECTED: compile-time error - int arithmetic result assigned to string local a: int = 10 local b: int = 20 local c: string = a + b return c ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_array_literal.diag.txt ================================================ ERROR: expression of type 'array' cannot be assigned to type 'table' testData/diagnostics/compilation_errors/type_inference/test_array_literal.nut:2:0 // EXPECTED: compile-time error - array literal assigned to table variable local x: table = [1, 2, 3] ^------------------------- return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_array_literal.nut ================================================ // EXPECTED: compile-time error - array literal assigned to table variable local x: table = [1, 2, 3] return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_array_return.diag.txt ================================================ ERROR: expression of type 'array' cannot be assigned to type 'table' testData/diagnostics/compilation_errors/type_inference/test_array_return.nut:3:4 function make_array(): table { return [1, 2, 3] ^--------------- } ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_array_return.nut ================================================ // EXPECTED: compile-time error - function returns array, declared as returning table function make_array(): table { return [1, 2, 3] } return make_array ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_array_to_int.diag.txt ================================================ ERROR: expression of type 'array' cannot be assigned to type 'int' testData/diagnostics/compilation_errors/type_inference/test_array_to_int.nut:2:0 // EXPECTED: compile-time error - array assigned to int local x: int = [1, 2, 3] ^----------------------- return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_array_to_int.nut ================================================ // EXPECTED: compile-time error - array assigned to int local x: int = [1, 2, 3] return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_assignment_chain.diag.txt ================================================ ERROR: expression of type 'int' cannot be assigned to type 'string' testData/diagnostics/compilation_errors/type_inference/test_assignment_chain.nut:4:0 let z = 120 * y let s: string = z ^---------------- ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_assignment_chain.nut ================================================ let x: int = 234 let y = x + 4 let z = 120 * y let s: string = z ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_call_result_type.diag.txt ================================================ ERROR: expression of type 'int' cannot be assigned to type 'string' testData/diagnostics/compilation_errors/type_inference/test_call_result_type.nut:5:0 } local y: string = sqri(5) ^------------------------ return y ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_call_result_type.nut ================================================ // EXPECTED: compile-time error - function returns int, assigned to string variable function sqri(x: int): int { return x * x } local y: string = sqri(5) return y ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_call_union_return.diag.txt ================================================ ERROR: expression of type 'int|string' cannot be assigned to type 'int' testData/diagnostics/compilation_errors/type_inference/test_call_union_return.nut:5:0 } local x: int = fn(1) ^------------------- return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_call_union_return.nut ================================================ // EXPECTED: compile-time error - fn returns string|int, but var only accepts int function fn(val): string|int { return val ? 42 : "hello" } local x: int = fn(1) return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_class_literal.diag.txt ================================================ ERROR: expression of type 'class' cannot be assigned to type 'string' testData/diagnostics/compilation_errors/type_inference/test_class_literal.nut:2:0 // EXPECTED: compile-time error - class literal assigned to string variable local x: string = class { ^ a = 1 ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_class_literal.nut ================================================ // EXPECTED: compile-time error - class literal assigned to string variable local x: string = class { a = 1 b = 2 } return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_comparison_to_int.diag.txt ================================================ ERROR: expression of type 'bool' cannot be assigned to type 'int' testData/diagnostics/compilation_errors/type_inference/test_comparison_to_int.nut:4:0 local b: int = 20 local c: int = a > b ^------------------- return c ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_comparison_to_int.nut ================================================ // EXPECTED: compile-time error - comparison produces bool, assigned to int local a: int = 10 local b: int = 20 local c: int = a > b return c ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_assign_mismatch.diag.txt ================================================ ERROR: expression of type 'string' cannot be assigned to type 'int' testData/diagnostics/compilation_errors/type_inference/test_complex_assign_mismatch.nut:4:0 local x: int = 42 x = "hello" ^ return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_assign_mismatch.nut ================================================ // EXPECTED: compile-time error // Variable with type annotation reassigned with wrong type expression local x: int = 42 x = "hello" return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_bitwise.diag.txt ================================================ ERROR: expression of type 'int' cannot be assigned to type 'string' testData/diagnostics/compilation_errors/type_inference/test_complex_bitwise.nut:8:0 local d: int = 0x55 local result: string = (a & b) | (c ^ d) << 2 ^-------------------------------------------- return result ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_bitwise.nut ================================================ // EXPECTED: compile-time error // Bitwise operations always produce int // (a & b) | (c ^ d) << 2 => int => not subset of string local a: int = 0xFF local b: int = 0x0F local c: int = 0xAA local d: int = 0x55 local result: string = (a & b) | (c ^ d) << 2 return result ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_call_chain.diag.txt ================================================ ERROR: expression of type 'float' cannot be assigned to type 'string' testData/diagnostics/compilation_errors/type_inference/test_complex_call_chain.nut:8:0 } local result: string = double_it(to_float(42)) ^--------------------------------------------- return result ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_call_chain.nut ================================================ // EXPECTED: compile-time error - chained function calls, final result is float, assigned to string function to_float(x: int): float { return x.tofloat() } function double_it(x: float): float { return x * 2.0 } local result: string = double_it(to_float(42)) return result ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_deep_nesting.diag.txt ================================================ ERROR: expression of type 'bool' cannot be assigned to type 'string' testData/diagnostics/compilation_errors/type_inference/test_complex_deep_nesting.nut:8:0 local c: int = 3 local deep_result: string = ((((a + b) * c) > 5) ? "big" : "small") != "big" ^--------------------------------------------------------------------------- return deep_result ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_deep_nesting.nut ================================================ // EXPECTED: compile-time error // Deeply nested expression with many operators // ((((1 + 2) * 3) > 5) ? "big" : "small") != "big" => bool // bool is not subset of string local a: int = 1 local b: int = 2 local c: int = 3 local deep_result: string = ((((a + b) * c) > 5) ? "big" : "small") != "big" return deep_result ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_logical.diag.txt ================================================ ERROR: expression of type 'int|string' cannot be assigned to type 'bool' testData/diagnostics/compilation_errors/type_inference/test_complex_logical.nut:4:0 // 42 || "hello" => int|string => not subset of bool local x: bool = 42 || "hello" ^---------------------------- return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_logical.nut ================================================ // EXPECTED: compile-time error // Logical operators: || returns one of the operands (not necessarily bool) // 42 || "hello" => int|string => not subset of bool local x: bool = 42 || "hello" return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_mega.diag.txt ================================================ ERROR: expression of type 'number|string' cannot be assigned to type 'function|null' testData/diagnostics/compilation_errors/type_inference/test_complex_mega.nut:53:0 // declared type: function|null => mismatch! local v_fail: function|null = get_flag() ? sqrf(4.3) : fn(3) ^----------------------------------------------------------- ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_mega.nut ================================================ // EXPECTED: compile-time error on line with v_fail // This test exercises many expression types in combination // // Type inference rules exercised: // - Literals: int, float, string, bool, null // - Arithmetic: int+int=int, float*float=float, int+float=int|float // - Comparison: always bool // - Typeof: always string // - Bitwise: always int // - Negation: ~int = int // - Logical not: !x = bool // - Ternary: union of branches // - Null-coalesce: (lhs minus null) | rhs // - Logical or/and: union of operands // - String concat: string + any = string // - Function call: return type of callee // - Table/array/class literals: table/array/class // - Lambda: function const function [pure] sqri(x: int): int { return x * x } const function [pure] sqrf(x: float): float { return x * x } function fn(val): string|int { return val ? sqri(val) : "err" } function get_flag(): bool { return true } function maybe_int(): int|null { return 42 } function make_table(): table { return { a = 1 } } function make_array(): array { return [1, 2] } // --- All of these should be OK --- local ok_int: int = sqri(5) // int local ok_float: float = sqrf(3.14) // float local ok_union: string|int = fn(42) // string|int local ok_bool: bool = get_flag() // bool local ok_nullc: int = maybe_int() ?? 0 // int local ok_ternary: int|float = get_flag() ? 42 : 3.14 // int|float local ok_arith: int = (1 + 2) * 3 - 4 % 5 // int local ok_bitwise: int = (0xFF & 0x0F) | (0xAA ^ 0x55) << 2 // int local ok_comp: bool = 42 > 10 // bool local ok_typeof: string = typeof [] // string local ok_not: bool = !get_flag() // bool local ok_table: table = { x = 1, y = "hello" } // table local ok_array: array = [1, "two", 3.0] // array local ok_class: class = class { val = 0 } // class local ok_lambda: function = @(x) x * 2 // function local ok_concat: string = "value: " + 42 // string (concat) local ok_deep: string = get_flag() ? typeof 42 : "fallback" // string|string = string local ok_chain: int|string = get_flag() ? sqri(10) : fn(5) // int | string|int = int|string local ok_wide: int|float|string|null = get_flag() ? (maybe_int() ?? sqri(1)) : fn(2) // --- This should FAIL --- // sqrf(4.3) returns float, fn(3) returns string|int // ternary: float | string|int = float|string|int // declared type: function|null => mismatch! local v_fail: function|null = get_flag() ? sqrf(4.3) : fn(3) return [ok_int, ok_float, ok_union, ok_bool] ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_mixed_arithmetic.diag.txt ================================================ ERROR: expression of type 'int|string' cannot be assigned to type 'bool' testData/diagnostics/compilation_errors/type_inference/test_complex_mixed_arithmetic.nut:7:0 local n: int = 10 local x: bool = get_flag() ? ((1 + 2) * 3 + n) : typeof "x" ^---------------------------------------------------------- return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_mixed_arithmetic.nut ================================================ // EXPECTED: compile-time error // Complex arithmetic expression with mixed types ending up assigned to wrong type // (1 + 2) * 3 => int, typeof "x" => string // result of ternary: int|string => not subset of bool function get_flag(): bool { return true } local n: int = 10 local x: bool = get_flag() ? ((1 + 2) * 3 + n) : typeof "x" return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_multifunction.diag.txt ================================================ ERROR: expression of type 'string|array' cannot be assigned to type 'int' testData/diagnostics/compilation_errors/type_inference/test_complex_multifunction.nut:12:4 function pick(flag: bool): int { return flag ? make_pair(1, 2) : get_name() ^----------------------------------------- } ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_multifunction.nut ================================================ // EXPECTED: compile-time error // Multiple functions with known return types composed together // make_pair returns array, get_name returns string // ternary: array|string => not subset of int function make_pair(a: int, b: int): array { return [a, b] } function get_name(): string { return "hello" } function pick(flag: bool): int { return flag ? make_pair(1, 2) : get_name() } return pick ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_nested_ternary.diag.txt ================================================ ERROR: expression of type 'number|string' cannot be assigned to type 'number' testData/diagnostics/compilation_errors/type_inference/test_complex_nested_ternary.nut:8:0 local flag2 = false local x: int|float = flag1 ? (flag2 ? 42 : 3.14) : "oops" ^-------------------------------------------------------- return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_nested_ternary.nut ================================================ // EXPECTED: compile-time error - nested ternaries produce int|float|string, var only accepts int|float // flag1 ? (flag2 ? 42 : 3.14) : "oops" // true-branch: flag2 ? 42 : 3.14 => int|float // false-branch: "oops" => string // combined: int|float|string => NOT subset of int|float local flag1 = true local flag2 = false local x: int|float = flag1 ? (flag2 ? 42 : 3.14) : "oops" return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_nullcoalesce.diag.txt ================================================ ERROR: expression of type 'int|string' cannot be assigned to type 'float' testData/diagnostics/compilation_errors/type_inference/test_complex_nullcoalesce.nut:6:0 function maybe_int(): int|null { return 42 } local x: float = maybe_int() ?? "default" ^---------------------------------------- return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_nullcoalesce.nut ================================================ // EXPECTED: compile-time error // null-coalescing: lhs is int|null, rhs is string // result type: int (non-null part of lhs) | string (rhs) = int|string // assigned to float => mismatch function maybe_int(): int|null { return 42 } local x: float = maybe_int() ?? "default" return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_pure_chain.diag.txt ================================================ ERROR: expression of type 'float' cannot be assigned to type 'function|null' testData/diagnostics/compilation_errors/type_inference/test_complex_pure_chain.nut:15:0 local v: function|null = sqrf(4.3) + 3 ^------------------------------------- return v ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_pure_chain.nut ================================================ // EXPECTED: compile-time error // Chain of pure function calls with type mismatch at the end // sqri returns int, sqrf returns float, fn returns string|int // sqrf(1.5) + 3 => float (float arithmetic) => not subset of function|null const function [pure] sqri(x: int): int { return x * x } const function [pure] sqrf(x: float): float { return x * x } function fn(val): string|int { return val ? sqri(val) : "error" } local v: function|null = sqrf(4.3) + 3 return v ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_return_expr.diag.txt ================================================ ERROR: expression of type 'int|string' cannot be assigned to type 'int' testData/diagnostics/compilation_errors/type_inference/test_complex_return_expr.nut:5:4 function compute(flag: bool, x: int, y: int): int { return flag ? x * y + 1 : "error" ^-------------------------------- } ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_return_expr.nut ================================================ // EXPECTED: compile-time error // Complex return expression: function declared to return int, // but the expression is a ternary with string branch function compute(flag: bool, x: int, y: int): int { return flag ? x * y + 1 : "error" } return compute ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_table_ternary.diag.txt ================================================ ERROR: expression of type 'table|array' cannot be assigned to type 'table' testData/diagnostics/compilation_errors/type_inference/test_complex_table_ternary.nut:5:4 function get_data(flag: bool): table { return flag ? { x = 1 } : [1, 2, 3] ^---------------------------------- } ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_complex_table_ternary.nut ================================================ // EXPECTED: compile-time error // Ternary produces table|array, assigned to table // The array branch violates the constraint function get_data(flag: bool): table { return flag ? { x = 1 } : [1, 2, 3] } return get_data ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_const_array.diag.txt ================================================ ERROR: expression of type 'array' cannot be assigned to type 'table' testData/diagnostics/compilation_errors/type_inference/test_const_array.nut:3:0 const ARR2 = [4, 3] let x: table = true ? ARR1 : ARR2 ^-------------------------------- return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_const_array.nut ================================================ const ARR1 = [1, null] const ARR2 = [4, 3] let x: table = true ? ARR1 : ARR2 return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_const_array2.diag.txt ================================================ ERROR: expression of type 'array' cannot be assigned to type 'table' testData/diagnostics/compilation_errors/type_inference/test_const_array2.nut:4:12 function fn(x: table = true ? ARR1 : ARR2): null {} ^---------------------------- ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_const_array2.nut ================================================ const ARR1 = [1, null] const ARR2 = [4, 3] function fn(x: table = true ? ARR1 : ARR2): null {} return fn({}) ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_const_array3.diag.txt ================================================ ERROR: expression of type 'table|array' cannot be assigned to type 'table' testData/diagnostics/compilation_errors/type_inference/test_const_array3.nut:4:12 function fn(x: table = true ? {} : ARR2): null {} ^-------------------------- ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_const_array3.nut ================================================ const ARR1 = [1, null] const ARR2 = [4, 3] function fn(x: table = true ? {} : ARR2): null {} return fn({}) ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_const_array4.diag.txt ================================================ ERROR: expression of type 'table|array' cannot be assigned to type 'table' testData/diagnostics/compilation_errors/type_inference/test_const_array4.nut:4:12 function fn(x: table = true ? ARR1 : {}): null {} ^-------------------------- ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_const_array4.nut ================================================ const ARR1 = [1, null] const ARR2 = [4, 3] function fn(x: table = true ? ARR1 : {}): null {} return fn({}) ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_const_function.diag.txt ================================================ ERROR: expression of type 'function' cannot be assigned to type 'array' testData/diagnostics/compilation_errors/type_inference/test_const_function.nut:3:0 const function fn2() {} let x: array = true ? fn1 : fn2 ^------------------------------ return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_const_function.nut ================================================ const function fn1() {} const function fn2() {} let x: array = true ? fn1 : fn2 return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_const_inline.diag.txt ================================================ ERROR: expression of type 'string' cannot be assigned to type 'int' testData/diagnostics/compilation_errors/type_inference/test_const_inline.nut:3:0 const MY_STR = "hello" local x: int = MY_STR ^-------------------- return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_const_inline.nut ================================================ // EXPECTED: compile-time error - const string assigned to int variable const MY_STR = "hello" local x: int = MY_STR return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_const_table.diag.txt ================================================ ERROR: expression of type 'table' cannot be assigned to type 'array' testData/diagnostics/compilation_errors/type_inference/test_const_table.nut:3:0 const TAB2 = {x = 4} let x: array = true ? TAB1 : TAB2 ^-------------------------------- return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_const_table.nut ================================================ const TAB1 = {} const TAB2 = {x = 4} let x: array = true ? TAB1 : TAB2 return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_destructure_array_default_mismatch.diag.txt ================================================ ERROR: expression of type 'string' cannot be assigned to type 'int' testData/diagnostics/compilation_errors/type_inference/test_destructure_array_default_mismatch.nut:4:5 // This CAN be checked at compile time because the default value is known. let [x: int = "hello"] = [42] ^--------------- return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_destructure_array_default_mismatch.nut ================================================ // EXPECTED: compile-time error - default value "hello" doesn't match declared type int // The default value is a string literal, and the declared type is int. // This CAN be checked at compile time because the default value is known. let [x: int = "hello"] = [42] return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_destructure_complex.diag.txt ================================================ ERROR: expression of type 'float' cannot be assigned to type 'string' testData/diagnostics/compilation_errors/type_inference/test_destructure_complex.nut:6:40 } let [x: int = 0, y: string = "default", z: string = 3.14] = get_data() ^--------------- return [x, y, z] ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_destructure_complex.nut ================================================ // EXPECTED: compile-time error - default value type doesn't match declared type // In array destructuring, the default for z is a float but declared as string function get_data(): array { return [1, "hello"] } let [x: int = 0, y: string = "default", z: string = 3.14] = get_data() return [x, y, z] ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_destructure_table_mismatch.diag.txt ================================================ AN ERROR HAS OCCURRED [type 'string' differs from the declared type 'int'] CALLSTACK *FUNCTION [__main__()] testData/diagnostics/compilation_errors/type_inference/test_destructure_table_mismatch.nut:5 LOCALS [x] "hello" [vargv] ARRAY=[] [this] NULL ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_destructure_table_mismatch.nut ================================================ // EXPECTED: runtime error - destructured value "hello" doesn't match declared type int // (Compile-time inference for destructuring is harder since the source is a table // expression where individual field types need to be tracked. Initially this may // remain a runtime check. Documenting desired eventual behavior.) let {x: int} = {x = "hello"} return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_field_access_unknown.diag.txt ================================================ ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_field_access_unknown.nut ================================================ // EXPECTED: no compile-time error (should fall back to runtime check) // Field access on tables/objects has unknown type at compile time local t = { value = 42 } local x: int = t.value // type of t.value is unknown, runtime check return null ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_freeze_type_mismatch.diag.txt ================================================ ERROR: expression of type 'table' cannot be assigned to type 'int' testData/diagnostics/compilation_errors/type_inference/test_freeze_type_mismatch.nut:2:0 // EXPECTED: compile-time error - freeze({}) returns table, assigned to int variable local x: int = freeze({a = 1}) ^----------------------------- return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_freeze_type_mismatch.nut ================================================ // EXPECTED: compile-time error - freeze({}) returns table, assigned to int variable local x: int = freeze({a = 1}) return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_function_literal.diag.txt ================================================ ERROR: expression of type 'function' cannot be assigned to type 'int' testData/diagnostics/compilation_errors/type_inference/test_function_literal.nut:2:0 // EXPECTED: compile-time error - lambda assigned to int variable local x: int = @() 42 ^-------------------- return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_function_literal.nut ================================================ // EXPECTED: compile-time error - lambda assigned to int variable local x: int = @() 42 return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_instance_from_class.diag.txt ================================================ ERROR: expression of type 'instance' cannot be assigned to type 'string' testData/diagnostics/compilation_errors/type_inference/test_instance_from_class.nut:6:0 } local obj: string = MyClass() ^---------------------------- return obj ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_instance_from_class.nut ================================================ // EXPECTED: compile-time error - class() produces instance, assigned to string local MyClass = class { x = 0 constructor() {} } local obj: string = MyClass() return obj ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_literal_float_to_int.diag.txt ================================================ ERROR: expression of type 'float' cannot be assigned to type 'int' testData/diagnostics/compilation_errors/type_inference/test_literal_float_to_int.nut:2:0 // EXPECTED: compile-time error - float literal assigned to int variable local x: int = 3.14 ^------------------ return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_literal_float_to_int.nut ================================================ // EXPECTED: compile-time error - float literal assigned to int variable local x: int = 3.14 return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_literal_int_to_string.diag.txt ================================================ ERROR: expression of type 'int' cannot be assigned to type 'string' testData/diagnostics/compilation_errors/type_inference/test_literal_int_to_string.nut:2:0 // EXPECTED: compile-time error - integer literal assigned to string variable local x: string = 42 ^------------------- return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_literal_int_to_string.nut ================================================ // EXPECTED: compile-time error - integer literal assigned to string variable local x: string = 42 return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_literal_string_to_int.diag.txt ================================================ ERROR: expression of type 'string' cannot be assigned to type 'int' testData/diagnostics/compilation_errors/type_inference/test_literal_string_to_int.nut:2:0 // EXPECTED: compile-time error - string literal assigned to int variable local x: int = "hello" ^--------------------- return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_literal_string_to_int.nut ================================================ // EXPECTED: compile-time error - string literal assigned to int variable local x: int = "hello" return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_return_float_to_int.diag.txt ================================================ ERROR: expression of type 'float' cannot be assigned to type 'int' testData/diagnostics/compilation_errors/type_inference/test_return_float_to_int.nut:3:4 function fn(): int { return 3.14 ^---------- } ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_return_float_to_int.nut ================================================ // EXPECTED: compile-time error - return float from int function function fn(): int { return 3.14 } return fn ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_return_literal_mismatch.diag.txt ================================================ ERROR: expression of type 'string' cannot be assigned to type 'int' testData/diagnostics/compilation_errors/type_inference/test_return_literal_mismatch.nut:3:4 function fn(): int { return "hello" ^------------- } ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_return_literal_mismatch.nut ================================================ // EXPECTED: compile-time error - return string from int function function fn(): int { return "hello" } return fn ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_slot_access_unknown.diag.txt ================================================ ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_slot_access_unknown.nut ================================================ // EXPECTED: no compile-time error (should fall back to runtime check) // Slot access (dynamic key) has unknown type at compile time local t = { value = 42 } local key = "value" local x: int = t[key] // type of t[key] is unknown, runtime check return null ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_string_concat_to_int.diag.txt ================================================ ERROR: expression of type 'string' cannot be assigned to type 'int' testData/diagnostics/compilation_errors/type_inference/test_string_concat_to_int.nut:4:0 local n: int = 42 local result: int = s + n ^------------------------ return result ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_string_concat_to_int.nut ================================================ // EXPECTED: compile-time error - string concat produces string, not int local s: string = "hello" local n: int = 42 local result: int = s + n return result ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_table_literal.diag.txt ================================================ ERROR: expression of type 'table' cannot be assigned to type 'array' testData/diagnostics/compilation_errors/type_inference/test_table_literal.nut:2:0 // EXPECTED: compile-time error - table literal assigned to array variable local x: array = { a = 1, b = 2 } ^-------------------------------- return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_table_literal.nut ================================================ // EXPECTED: compile-time error - table literal assigned to array variable local x: array = { a = 1, b = 2 } return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_table_return.diag.txt ================================================ ERROR: expression of type 'table' cannot be assigned to type 'array' testData/diagnostics/compilation_errors/type_inference/test_table_return.nut:3:4 function make_table(): array { return { a = 1, b = 2 } ^---------------------- } ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_table_return.nut ================================================ // EXPECTED: compile-time error - function returns table, declared as returning array function make_table(): array { return { a = 1, b = 2 } } return make_table ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_table_to_string.diag.txt ================================================ ERROR: expression of type 'table' cannot be assigned to type 'string' testData/diagnostics/compilation_errors/type_inference/test_table_to_string.nut:2:0 // EXPECTED: compile-time error - table assigned to string local x: string = { a = 1 } ^-------------------------- return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_table_to_string.nut ================================================ // EXPECTED: compile-time error - table assigned to string local x: string = { a = 1 } return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_ternary_mismatch.diag.txt ================================================ ERROR: expression of type 'int|string' cannot be assigned to type 'int' testData/diagnostics/compilation_errors/type_inference/test_ternary_mismatch.nut:3:0 local flag = true local x: int = flag ? 42 : "hello" ^--------------------------------- return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_ternary_mismatch.nut ================================================ // EXPECTED: compile-time error - ternary produces int|string, var only accepts int local flag = true local x: int = flag ? 42 : "hello" return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_typeof_to_int.diag.txt ================================================ ERROR: expression of type 'string' cannot be assigned to type 'int' testData/diagnostics/compilation_errors/type_inference/test_typeof_to_int.nut:2:0 // EXPECTED: compile-time error - typeof produces string, not int local x: int = typeof 42 ^----------------------- return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_typeof_to_int.nut ================================================ // EXPECTED: compile-time error - typeof produces string, not int local x: int = typeof 42 return x ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_unknown_passthrough.diag.txt ================================================ ================================================ FILE: testData/diagnostics/compilation_errors/type_inference/test_unknown_passthrough.nut ================================================ // EXPECTED: no compile-time error (should fall back to runtime check) // When the expression type is unknown (~0u), the compiler cannot check // and must emit _OP_CHECK_TYPE for runtime. This test verifies no false positive. function unknown_fn(x) { // no return type annotation return x } local x: int = unknown_fn(42) // unknown return type, runtime check only return null ================================================ FILE: testData/diagnostics/const_scope_1.diag.txt ================================================ ERROR: Unknown variable [x] testData/diagnostics/const_scope_1.nut:4:6 print(x) ^ ================================================ FILE: testData/diagnostics/const_scope_1.nut ================================================ if (true) const x = 5 print(x) ================================================ FILE: testData/diagnostics/const_scope_2.diag.txt ================================================ ERROR: Unknown variable [x] testData/diagnostics/const_scope_2.nut:5:6 print(x) ^ ================================================ FILE: testData/diagnostics/const_scope_2.nut ================================================ local function f() { const x = 6 } print(x) ================================================ FILE: testData/diagnostics/const_scope_3.diag.txt ================================================ ERROR: Unknown variable [x] testData/diagnostics/const_scope_3.nut:4:8 else print(x) ^ ================================================ FILE: testData/diagnostics/const_scope_3.nut ================================================ if (true) const x = 6 else print(x) ================================================ FILE: testData/diagnostics/continue_in_codeblock.diag.txt ================================================ ERROR: 'continue' has to be in a loop block testData/diagnostics/continue_in_codeblock.nut:3:2 let x = $${ continue ^------- return 1 ================================================ FILE: testData/diagnostics/continue_in_codeblock.nut ================================================ #allow-compiler-internals let x = $${ continue return 1 } ================================================ FILE: testData/diagnostics/delete_base.diag.txt ================================================ ERROR: can't delete 'base' testData/diagnostics/delete_base.nut:2:0 #allow-delete-operator delete base ^---------- ================================================ FILE: testData/diagnostics/delete_base.nut ================================================ #allow-delete-operator delete base // ^ Legacy, to be removed ================================================ FILE: testData/diagnostics/delete_forbid_pragma.diag.txt ================================================ ERROR: Usage of 'delete' operator is forbidden. Use 'o.rawdelete("key")' instead testData/diagnostics/delete_forbid_pragma.nut.txt:10:0 delete t.a // FP 1 ^----- ================================================ FILE: testData/diagnostics/delete_forbid_pragma.nut.txt ================================================ let t = { a = 10, b = 20, c = 30, d = 40 } delete t.a // FP 1 #forbid-delete-operator t.rawdelete("b") #allow-delete-operator delete t.c // FP 2 #forbid-delete-operator delete t.d // EXPECTED ================================================ FILE: testData/diagnostics/destrucuring_var_decl_in_if.diag.txt ================================================ ERROR: Only single variable declaration is allowed here testData/diagnostics/destrucuring_var_decl_in_if.nut:1:10 if (local [x] = [1, 2, 3]; x > 4) ^ println("fail") ================================================ FILE: testData/diagnostics/destrucuring_var_decl_in_if.nut ================================================ if (local [x] = [1, 2, 3]; x > 4) println("fail") ================================================ FILE: testData/diagnostics/float_overflow.diag.txt ================================================ ERROR: float constant overflow testData/diagnostics/float_overflow.nut:1:8 println(1000000000000000000000000000000000000000.0) ^----------------------------------------- ================================================ FILE: testData/diagnostics/float_overflow.nut ================================================ println(1000000000000000000000000000000000000000.0) ================================================ FILE: testData/diagnostics/float_underflow.diag.txt ================================================ ERROR: float constant underflow testData/diagnostics/float_underflow.nut:1:8 println(0.00000000000000000000000000000000000000000000000000000000000000001) ^------------------------------------------------------------------ ================================================ FILE: testData/diagnostics/float_underflow.nut ================================================ println(0.00000000000000000000000000000000000000000000000000000000000000001) ================================================ FILE: testData/diagnostics/foreach_destr_typed_default.diag.txt ================================================ ERROR: expression of type 'string' cannot be assigned to type 'int' testData/diagnostics/foreach_destr_typed_default.nut:1:10 foreach ({x: int = "bad"} in [{x = 1}]) { ^------------- println(x) ================================================ FILE: testData/diagnostics/foreach_destr_typed_default.nut ================================================ foreach ({x: int = "bad"} in [{x = 1}]) { println(x) } ================================================ FILE: testData/diagnostics/hex_overflow.diag.txt ================================================ ERROR: too many digits for a hex number testData/diagnostics/hex_overflow.nut:1:8 println(0x1_0000_0000_0000_0000) ^---------------------- ================================================ FILE: testData/diagnostics/hex_overflow.nut ================================================ println(0x1_0000_0000_0000_0000) ================================================ FILE: testData/diagnostics/if_var_decl_init.diag.txt ================================================ ERROR: Initialization required testData/diagnostics/if_var_decl_init.nut:1:11 if (local x) ^ println("fail") ================================================ FILE: testData/diagnostics/if_var_decl_init.nut ================================================ if (local x) println("fail") ================================================ FILE: testData/diagnostics/import_01.diag.txt ================================================ ================================================ FILE: testData/diagnostics/import_01.nut ================================================ import "a" ================================================ FILE: testData/diagnostics/import_02.diag.txt ================================================ ================================================ FILE: testData/diagnostics/import_02.nut ================================================ /* wretwert rtwrt */ // wtwrtwrt wrtwr t\ /* adsfadf/* // */ import /**/ "a" /**/ as x /* wretwert rtwrt */ ================================================ FILE: testData/diagnostics/import_03.diag.txt ================================================ ERROR: missing "*/" in comment testData/diagnostics/import_03.nut:1:0 /* ^-------------------- import "a" as x ================================================ FILE: testData/diagnostics/import_03.nut ================================================ /* import "a" as x ================================================ FILE: testData/diagnostics/import_04.diag.txt ================================================ ERROR: expected 'identifier' testData/diagnostics/import_04.nut:2:1 import "a" as ^ ================================================ FILE: testData/diagnostics/import_04.nut ================================================ import "a" as ================================================ FILE: testData/diagnostics/import_05.diag.txt ================================================ ERROR: unrecognised escape char testData/diagnostics/import_05.nut:1:5 from "../a\..\b\folder/file.nut" import * ^----- ================================================ FILE: testData/diagnostics/import_05.nut ================================================ from "../a\..\b\folder/file.nut" import * ================================================ FILE: testData/diagnostics/import_06.diag.txt ================================================ ================================================ FILE: testData/diagnostics/import_06.nut ================================================ #strict // import b import "math" // as math2 import "dagor2.math" as m from "generic/std.nut" import TMatrix as Matrix, /* Xyz as Qwe */ Point2 as point /* comment comment // */ from "module2" import * local a = require_optional("x.nut") return a?.x ================================================ FILE: testData/diagnostics/import_07.diag.txt ================================================ ERROR: newline in a constant testData/diagnostics/import_07.nut:1:7 import "a ^-- " as a ================================================ FILE: testData/diagnostics/import_07.nut ================================================ import "a " as a ================================================ FILE: testData/diagnostics/import_error.diag.txt ================================================ ERROR: expected 'identifier or *' testData/diagnostics/import_error.nut:2:20 // missing import list from "file1" import ^ ================================================ FILE: testData/diagnostics/import_error.nut ================================================ // missing import list from "file1" import ================================================ FILE: testData/diagnostics/init_with_wrong_type.diag.txt ================================================ ERROR: Assigned null type differs from the declared type testData/diagnostics/init_with_wrong_type.nut:1:0 local y: table ^------ return y ================================================ FILE: testData/diagnostics/init_with_wrong_type.nut ================================================ local y: table return y ================================================ FILE: testData/diagnostics/integer_overflow.diag.txt ================================================ ERROR: integer constant overflow testData/diagnostics/integer_overflow.nut:1:8 println(1000000000000000000000000000000000000000) ^--------------------------------------- ================================================ FILE: testData/diagnostics/integer_overflow.nut ================================================ println(1000000000000000000000000000000000000000) ================================================ FILE: testData/diagnostics/integer_overflow_2.diag.txt ================================================ ERROR: integer constant overflow testData/diagnostics/integer_overflow_2.nut:1:8 println(20496385583567863808) ^------------------- ================================================ FILE: testData/diagnostics/integer_overflow_2.nut ================================================ println(20496385583567863808) ================================================ FILE: testData/diagnostics/integer_overflow_int_max.diag.txt ================================================ ERROR: integer constant overflow testData/diagnostics/integer_overflow_int_max.nut:1:8 println(9223372036854775808) ^------------------ ================================================ FILE: testData/diagnostics/integer_overflow_int_max.nut ================================================ println(9223372036854775808) ================================================ FILE: testData/diagnostics/interp_str_hanging_missed_ccurvy.diag.txt ================================================ ERROR: unfinished string testData/diagnostics/interp_str_hanging_missed_ccurvy.nut:3:16 println($"{foo()") ^- ================================================ FILE: testData/diagnostics/interp_str_hanging_missed_ccurvy.nut ================================================ function foo() { return "X" } println($"{foo()") ================================================ FILE: testData/diagnostics/interp_str_not_string.diag.txt ================================================ ERROR: expected 'expression' testData/diagnostics/interp_str_not_string.nut:3:8 println($'{{{{{{{{') ^ ================================================ FILE: testData/diagnostics/interp_str_not_string.nut ================================================ println($'{{{{{{{{') ================================================ FILE: testData/diagnostics/interp_str_not_string_2.diag.txt ================================================ ERROR: expected 'expression' testData/diagnostics/interp_str_not_string_2.nut:5:8 println($foo()) ^ ================================================ FILE: testData/diagnostics/interp_str_not_string_2.nut ================================================ function foo() {} println($foo()) ================================================ FILE: testData/diagnostics/interp_str_wrong_template.diag.txt ================================================ ERROR: expected 'IDENTIFIER' testData/diagnostics/interp_str_wrong_template.nut:3:12 println($"{{{{{{{{") ^ ================================================ FILE: testData/diagnostics/interp_str_wrong_template.nut ================================================ println($"{{{{{{{{") ================================================ FILE: testData/diagnostics/invalid_float_1.diag.txt ================================================ ERROR: malformed number testData/diagnostics/invalid_float_1.nut:1:8 println(1e4e-4) ^-- ================================================ FILE: testData/diagnostics/invalid_float_1.nut ================================================ println(1e4e-4) ================================================ FILE: testData/diagnostics/invalid_float_2.diag.txt ================================================ ERROR: malformed number testData/diagnostics/invalid_float_2.nut:1:8 println(1e4......) ^-- ================================================ FILE: testData/diagnostics/invalid_float_2.nut ================================================ println(1e4......) ================================================ FILE: testData/diagnostics/invalid_float_3.diag.txt ================================================ ERROR: malformed number testData/diagnostics/invalid_float_3.nut:1:8 println(1.2.3.4) ^-- ================================================ FILE: testData/diagnostics/invalid_float_3.nut ================================================ println(1.2.3.4) ================================================ FILE: testData/diagnostics/invalid_string_interp_1.diag.txt ================================================ ERROR: unfinished string testData/diagnostics/invalid_string_interp_1.nut:1:11 println($"{") ^- ================================================ FILE: testData/diagnostics/invalid_string_interp_1.nut ================================================ println($"{") ================================================ FILE: testData/diagnostics/invalid_type_hint_1.diag.txt ================================================ ERROR: expected 'TYPE_NAME' testData/diagnostics/invalid_type_hint_1.nut:1:6 let a:2 = 3 ^ ================================================ FILE: testData/diagnostics/invalid_type_hint_1.nut ================================================ let a:2 = 3 ================================================ FILE: testData/diagnostics/invalid_type_hint_2.diag.txt ================================================ ERROR: expected 'TYPE_NAME' testData/diagnostics/invalid_type_hint_2.nut:1:16 function fn(...:) {} ^ ================================================ FILE: testData/diagnostics/invalid_type_hint_2.nut ================================================ function fn(...:) {} ================================================ FILE: testData/diagnostics/invalid_type_hint_3.diag.txt ================================================ ERROR: Invalid type name 'Integer', did you mean 'int'? testData/diagnostics/invalid_type_hint_3.nut:1:13 let a = @(x: Integer): Integer x * x ^------ ================================================ FILE: testData/diagnostics/invalid_type_hint_3.nut ================================================ let a = @(x: Integer): Integer x * x ================================================ FILE: testData/diagnostics/leading_zero_1.diag.txt ================================================ ERROR: leading 0 is not allowed, octal numbers are not supported testData/diagnostics/leading_zero_1.nut:1:6 print(00) ^ ================================================ FILE: testData/diagnostics/leading_zero_1.nut ================================================ print(00) ================================================ FILE: testData/diagnostics/leading_zero_2.diag.txt ================================================ ERROR: leading 0 is not allowed, octal numbers are not supported testData/diagnostics/leading_zero_2.nut:1:6 print(01) ^ ================================================ FILE: testData/diagnostics/leading_zero_2.nut ================================================ print(01) ================================================ FILE: testData/diagnostics/leading_zero_3.diag.txt ================================================ ERROR: leading 0 is not allowed, octal numbers are not supported testData/diagnostics/leading_zero_3.nut:1:6 print(00.1) ^ ================================================ FILE: testData/diagnostics/leading_zero_3.nut ================================================ print(00.1) ================================================ FILE: testData/diagnostics/letAssign.diag.txt ================================================ ERROR: can't assign to binding 'y' (probably declaring using 'local' was intended, but 'let' was used) testData/diagnostics/letAssign.nut:4:0 let y = 30 y = 40 ^ ================================================ FILE: testData/diagnostics/letAssign.nut ================================================ local x = 10 x = 20 let y = 30 y = 40 ================================================ FILE: testData/diagnostics/many_args_in_function_call.diag.txt ================================================ ERROR: internal compiler error: too many locals testData/diagnostics/many_args_in_function_call.nut:-1:-1 ================================================ FILE: testData/diagnostics/many_args_in_function_call.nut ================================================ local function f(...) {} f(1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 ) ================================================ FILE: testData/diagnostics/need_space_after_float.diag.txt ================================================ ERROR: malformed number testData/diagnostics/need_space_after_float.nut:2:9 local f = 333 println([5.0f][1]) ^-- ================================================ FILE: testData/diagnostics/need_space_after_float.nut ================================================ local f = 333 println([5.0f][1]) ================================================ FILE: testData/diagnostics/need_space_after_hex.diag.txt ================================================ ERROR: expected hex digits after '0x' testData/diagnostics/need_space_after_hex.nut:2:9 let x = 123 println([0xx][1]) ^- ================================================ FILE: testData/diagnostics/need_space_after_hex.nut ================================================ let x = 123 println([0xx][1]) ================================================ FILE: testData/diagnostics/need_space_after_int.diag.txt ================================================ ERROR: malformed number testData/diagnostics/need_space_after_int.nut:2:9 local f = 333 println([5f][1]) ^ ================================================ FILE: testData/diagnostics/need_space_after_int.nut ================================================ local f = 333 println([5f][1]) ================================================ FILE: testData/diagnostics/return_type_check_1.diag.txt ================================================ ERROR: Return type differs from the declared type testData/diagnostics/return_type_check_1.nut:4:8 if (x < 0) return ^----- return 4 ================================================ FILE: testData/diagnostics/return_type_check_1.nut ================================================ function fn(x: int): int { if (x < 0) return return 4 } return fn ================================================ FILE: testData/diagnostics/single_var_decl_in_if.diag.txt ================================================ ERROR: Only single variable declaration is allowed here testData/diagnostics/single_var_decl_in_if.nut:1:13 if (let x = 4, y = 6) ^ println("fail") ================================================ FILE: testData/diagnostics/single_var_decl_in_if.nut ================================================ if (let x = 4, y = 6) println("fail") ================================================ FILE: testData/diagnostics/space_sep_name.diag.txt ================================================ ERROR: Separate access operator '.' with its following name is forbidden testData/diagnostics/space_sep_name.nut.txt:10:0 a1. append(8). // NOT OK ^----- extend(6) // NOT OK ================================================ FILE: testData/diagnostics/space_sep_name.nut.txt ================================================ let a1 = [1, 2, 3] a1.append(6) // OK .extend(7) // OK a1. append(8). // NOT OK extend(6) // NOT OK ================================================ FILE: testData/diagnostics/space_sep_name_space.diag.txt ================================================ ERROR: Separate access operator '?.$' with its following name is forbidden testData/diagnostics/space_sep_name_space.nut.txt:8:10 t1?.$ foo // NOT OK ^-- ================================================ FILE: testData/diagnostics/space_sep_name_space.nut.txt ================================================ let t1 = {} t1 ?.foo // OK .bar // OK t1?.$ foo // NOT OK ================================================ FILE: testData/diagnostics/too_large_static_memo_expr.diag.txt ================================================ ERROR: static expression is too big testData/diagnostics/too_large_static_memo_expr.nut.txt:3:14 let x = static( ^ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ ================================================ FILE: testData/diagnostics/too_large_static_memo_expr.nut.txt ================================================ function a() { return 1 } let x = static( (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ (a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+(a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a()+a())+ 1 ) return x ================================================ FILE: testData/diagnostics/too_many_locals2.diag.txt ================================================ ERROR: internal compiler error: too many locals testData/diagnostics/too_many_locals2.nut:-1:-1 ================================================ FILE: testData/diagnostics/too_many_locals2.nut ================================================ //#disable-optimizer #allow-switch-statement let _sv = @(_e) (type(_e) == "integer" || type(_e) == "float" || type(_e) == "null" || type(_e) == "bool" || type(_e) == "string") ? "" + _e : type(_e) try { // Code Bricks assembly - seed 126 const function [pure] v0_fn(x) { return x + 0 } const v0_a = -65536 const v0 = v0_fn(v0_a) // [const_chain] local v1 = v0 v1 *= 32769 // [modify_mul_const] let v2_C = class { _n = 0 constructor(n) { this._n = n & 7 } function _nexti(idx) { local next = idx == null ? 0 : idx + 1 return next < this._n ? next : null } function _get(idx) { return idx * -257 } } let v2_obj = v2_C(v0) local v2 = 0 foreach (_i, v in v2_obj) v2 += v // [meta_nexti] global enum S126_E1 { A = "\u0000", B = "world" } let v3 = $"{S126_E1.A}{S126_E1.B}" // [enum_string] let v4_outer = {inner = {value = 42}} let v4 = v4_outer?.inner?.value ?? 4294967295 // [nullsafe_nested_table] let v5 = (function() { let g = (function() { for (local i = 0; i < 1; i++) yield i })() local s = 0 while (g.getstatus() == "suspended") { s += resume g } return s })() // [coroutine_sum] let v6_C = class { _v = 0 constructor(xv) { this._v = xv } } let v6_obj = v6_C(v1) let v6 = v6_obj instanceof v6_C // [instanceof_check] let v7 = ~v4 // [int_bitnot] local v8_root = {child = {data = {count = 32767, value = v5}}} local v8_data = v8_root.child.data v8_data.count += '9' v8_data.value *= 1000 let v8 = v8_root.child.data.count + v8_root.child.data.value // [modify_via_local_ref] let v9_fn = function(x, acc = []) { acc.append(x) return acc.len() } let v9_a = v9_fn(v7) let v9_b = v9_fn(v7 + 1) let v9 = v9_a + v9_b // [default_param_mutable_shared] let v10_fn = function(x, pipe = {pre = @(v) v & 0xFF, run = @(v) v + 129, post = @(v) v * -255}) { return pipe.post(pipe.run(pipe.pre(x))) } let v10 = v10_fn(v1) // [default_param_pipeline] let v11_C = class { _v = 0 constructor(xv) { this._v = xv } } let v11_obj = v11_C(v10) let v11 = v11_obj instanceof v11_C // [instanceof_check] let v12 = v0 < v8 ? v0 : v8 // [int_min_two] let v13 = v2 + v10 // [int_add] local v14 = 0 local v14_v = v0 & 0xFF while (v14_v != 0) { v14 += v14_v & 1 v14_v = v14_v >>> 1 } // [int_bit_count] let v15 = v3.len() // [string_len] let v16 = v10 assert(v16 == v16) // [assert_tautology] let v17_fn = function(p0, p1, p2) { local u0 = 9223372036854775807 try { u0 = p1.tointeger() } catch (_e) {} // [string_tointeger] local u1_state = 0 let u1_inc = function() { u1_state++; return u1_state } u1_inc() u1_inc() let u1 = u1_inc() // [closure_counter] let u2 = p1.indexof(v3) ?? 129 // [string_indexof] local u3 = typeof p2 switch (u3) { case "integer": u3 = "int"; break case "float": u3 = "flt"; break case "string": u3 = "str"; break default: u3 = "other" } // [typeof_switch] let u4_fn = function(x, ctx = { cls = class { v = 0; constructor(n) { this.v = n } }, wrap = @(inst) inst.v + -9223372036854775807 }) { return ctx.wrap(ctx.cls(x)) } let u4 = u4_fn(v14) // [default_param_table_class] let u5_fns = [@(x) x + -130, @(x) x * 127, @(x) x - -257] local u5 = u1 foreach (f in u5_fns) u5 = f(u5) // [array_of_closures_accumulate] let u6 = typeof u4 == "integer" // [typeof_int_check] let u7 = (@(a, b) a + b)(u3, p1) // [lambda_str_concat] return u7 } let v17 = v17_fn(v5, v3, v13) // [nested_func depth=1] let v18_fn = function(n) { local i = 0 while (i < 8) { if (i * n > -127) return i i++ } return i } let v18 = v18_fn(v7) // [while_early_return] let v19 = v0.tostring() // [int_to_string] let v20 = v3.indexof(v17) ?? -65537 // [string_indexof] let v21 = v6 ? v13 : v5 // [ternary_int] let v22_outer = function(x) { let inner = @(n) n * -65535 return inner(x) + 4294967297 } let v22 = v22_outer(v0) // [nested_function] let v23_fn = function(p0, p1, p2) { let u0 = __LINE__ // [magic_line] let u1 = -v0 // [int_neg] local u2 = 1 local u2_i = 0 while (u2_i < 1) { u2 = u2 * ((v0 % 4) + 1) u2_i++ } // [while_mul] local u3 = false try { u3 = u1 == v7 } catch (_e) {} // [any_cmp_eq] local u4 = v13 ++u4 // [prefix_inc] let u5 = v3.rstrip() // [string_rstrip] return u5 } let v23 = v23_fn(v12, v19, v6) // [nested_func depth=1] local v24_iseven = null local v24_isodd = null v24_iseven = @(n) (n & 7) == 0 ? true : v24_isodd(n - 1) v24_isodd = @(n) (n & 7) == 0 ? false : v24_iseven(n - 1) let v24 = v24_iseven(v5 & 7) ? -3 : 255 // [mutual_recursion] let v25 = v19.len() // [string_len] let v26_fn = function(...) { return vargv.len() > 0 ? vargv[0] : 256 } let v26 = v26_fn(v8) // [vargv_first] local v27 = 0 switch (v24 & 3) { case 0: case 1: v27 = -2147483648; break case 2: v27 = -3; break default: v27 = '\t' } // [switch_fallthrough] local v28 = -4294967295 if (v26 > v13) { v28 = 65536 } else if (v26 == v13) { v28 = 0 } else { v28 = -32768 } // [if_chain] let v29 = !v11 // [bool_not] let v30_fn = @(a: int, b: int) a * b let v30 = v30_fn(v18, v21) // [typed_lambda] local v31 = v0 v31++ // [postfix_inc] local v32 = -127 switch (v25 % 3) { case 0: v32 = -255; break case 1: v32 = 0xFF; break default: v32 = -32769; break } // [switch_default] local v33 = v9 v33 += v28 v33 -= v22 v33 *= -4294967298 // [modify_chain] let v34_fn = function({dx, dy = -9223372036854775807}, [da, db]) { return dx + dy + da + db } let v34 = v34_fn({dx = v24}, [v15, v18]) // [destruct_param_mixed] local v35_sum = 0 let v35_add = function(x) { v35_sum = v35_sum + x } v35_add(v24) v35_add(v7) v35_add(v24 + v7) let v35 = v35_sum // [closure_accumulator] let [v36_a, v36_b] = [v5, v5 + 1] let [v36_c] = [v36_a + v36_b] let v36 = v36_c // [destruct_nested] let v37_fn = function({da, db}) { return da + db } let v37 = v37_fn({da = v30, db = v27}) // [destruct_param_table] let v38_fn = function(x, cls = class { function calc(v) { return v + 4294967295 } }) { return cls().calc(x) } let v38_a = v38_fn(v4) let v38_other = class { function calc(v) { return v * 0x100 } } let v38_b = v38_fn(v4, v38_other) let v38 = v38_a + v38_b // [default_param_class] let v39_fn = function(p0, p1) { local u0 = -32770 switch (v17) { case "a": u0 = 42; break case "b": u0 = -2147483647; break case "c": u0 = 1; break default: u0 = -257 } // [switch_string] let u1_fn = function(n) { local x = n & 7 if (x <= 0) return 0 return x + callee()(x - 1) } let u1 = u1_fn(v7) // [func_recursive_sum] local u2 = v0 u2 += 0xDEADBEEF // [modify_add_const] let u3_fn = function() { let w0_t = {a = {b = {c = v36}}} let w0 = w0_t?.a?.b?.c ?? -1 // [table_deep] let w1_fns = [@(x) x + 256, @(x) x * 65536, @(x) x - -257] local w1 = v1 foreach (f in w1_fns) w1 = f(w1) // [array_of_closures_accumulate] let w2_idx = v17.indexof(v23) let w2 = w2_idx != null ? w2_idx : 0x80000000 // [string_find_method] local w3 = 0 for (local w3_i = 0; w3_i < 2; w3_i++) { w3 += v30 if (w3 >= (v13 & 0x7FFFFFFF)) break } // [for_break_early] return w3 } let u3 = u3_fn() // [nested_func depth=2] let u4_fn = function() { let w0 = {a = null, b = null} // [null_table_value] let w1_make = @(bv) @(x) x + bv let w1_add = w1_make(v12) let w1 = w1_add(32768) // [func_returns_func] let w2_fn = function(x: int): string|null { return x > -32770 ? "[]{}()" : null } let w2 = w2_fn(v13) != null // [typed_return_null] function [pure] w3_fn(x) { return x * 65535 } let w3 = w3_fn(v12) // [named_func_pure] const function [pure] w4_fn(a, b) { return a + b * 65536 } const w4 = w4_fn(-4294967298, 3) // [const_pure_call_multi] return w4 } let u4 = u4_fn() // [nested_func depth=2] let u5_fn = function() { local w0 = "" if (v11) { w0 = v19 } else { w0 = v23 } // [if_else_str] let w1_t = {x = v25} w1_t.x *= -9223372036854775807 let w1 = w1_t.x // [table_field_muleq] local w2 = v30 w2++ w2++ w2-- // [inc_dec_chain] return w2 } let u5 = u5_fn() // [nested_func depth=2] let u6_C = class { v = 0 constructor(x) { this.v = x } function get() { return this.v } } local u6_holder = {obj = u6_C(v31), tag = "\uffff"} let u6 = u6_holder?.obj?.get() ?? 2147483647 // [table_with_instance_field] return u6 } let v39 = v39_fn(v18, v16) // [nested_func depth=1] local v40 = -4 switch (v3) { case "a": v40 = 1000; break case "b": v40 = -127; break case "c": v40 = 256; break default: v40 = ' ' } // [switch_string] local v41 = v9 & 7 do { v41-- } while (v41 > 0) // [do_while_dec] global enum S126_E2 { V1 = 3, V2 = -32769 } let v42_t = {a = S126_E2.V1, b = S126_E2.V2} let v42 = v42_t.a + v42_t.b // [enum_in_table] let v43_fn = function(x: int|null): int { return x != null ? x : 0x80000000 } let v43 = v43_fn(v8) // [typed_nullable_param] local v44 = v25 & 7 do { v44-- } while (v44 > 0) // [do_while_dec] let v45_fn = function(p0, p1) { let u0_fn = function(n) { local i = 0 while (i < -1) { if (i * n > 3) return i i++ } return i } let u0 = u0_fn(v34) // [while_early_return] let u1_fn = function(x: int): int { return x * 1000 } let u1 = u1_fn(v40) // [typed_function_return] let u2_C = class { _a = 0 _b = 0 constructor(xa, xb) { this._a = xa; this._b = xb } function sum() { return this._a + this._b } function diff() { return this._a - this._b } } let u2_obj = u2_C(v15, v2) let u2 = u2_obj.sum() + u2_obj.diff() // [class_method_call] let u3_fn = function(x: int|null): int { return x != null ? x : -2 } let u3 = u3_fn(v22) // [typed_nullable_param] let u4_fn = function(a, b, combine = {op = @(x, y) x + y, identity = -3}) { return combine.op(a, b) + combine.identity } let u4_add = u4_fn(v4, v26) let u4_mul = u4_fn(v4, v26, {op = @(x, y) x * y, identity = -4294967295}) let u4 = u4_add + u4_mul // [default_param_fn_field_call] let u5 = (function() { let gen = (function() { yield 0 yield -32769 yield 32769 })() let arr = [] while (gen.getstatus() == "suspended") { arr.append(resume gen) } return arr })() // [coroutine_collect_all] return u4 } let v45 = v45_fn(v39, v25) // [nested_func depth=1] let v46 = {} // [empty_table] let v47_t = {arr = [0, null, {key = v39}]} let v47 = v47_t?.arr?[2]?.key ?? 42 // [nested_table_array_nullsafe_read] let v48_fn = function(x, s, cfg = { fmt = @(n, t) $"{n}:{t}", limits = {lo = -2147483648, hi = -65535} }) { local clamped = x < cfg.limits.lo ? cfg.limits.lo : x > cfg.limits.hi ? cfg.limits.hi : x return cfg.fmt(clamped, s) } let v48 = v48_fn(v9, v17) // [default_param_config_nested] local v49 = 32769 if (let v49_v = v46?.x; v49_v != null) { v49 = v49_v } // [if_let_nullcheck] local v50 = 0 foreach (c in v48) { if (c >= '0' && c <= '9') v50++ } // [string_scan_digits] local v51 = 2147483648 if (v29) { v51 = v2 + 128 } else { v51 = v12 - 4294967297 } // [if_else_int] local v52 = "" foreach (c in v17) { if (c != ' ') v52 += c.tochar() } // [string_build_filtered] const function [pure] v53_fn(x) { return x + 0xDEADBEEF } const v53_a = 0x1 const v53 = v53_fn(v53_a) // [const_chain] const v54 = -32769 + -255 // [const_expr] let v55 = typeof null == "null" // [typeof_null_check] const function [pure] v56_fn(x) { return x * -1 } const v56 = v56_fn(4294967296) // [const_pure_call] let v57_fn = function(p0, p1) { let u0 = [] local u0_i = 0 while (u0_i < 3) { u0.append(u0_i * u0_i) u0_i++ } // [while_array_fill] let u1_fn = function(x, ctx = { cls = class { v = 0; constructor(n) { this.v = n } }, wrap = @(inst) inst.v + -4294967295 }) { return ctx.wrap(ctx.cls(x)) } let u1 = u1_fn(v9) // [default_param_table_class] let u2_fn = function() { local w0 = 0 for (local w0_i = 7; w0_i > 0; w0_i--) { w0 += v56 } // [for_countdown] local w1 = "" local w1_i = 0 while (w1_i < 1) { w1 = w1 + v3 w1_i++ } // [while_string_build] local w2 = 0 u0.each(@(_v) w2++) // [array_each_lambda] return w2 } let u2 = u2_fn() // [nested_func depth=2] let u3 = v9.tofloat() // [int_to_float] let u4 = u0.findvalue(@(v) v != null) ?? 2147483648 // [array_findvalue] let u5 = array(v38 & 7, 128) // [array_function_init] return u4 } let v57 = v57_fn(v50, v7) // [nested_func depth=1] local v58_state = 0 let v58_inc = function() { v58_state++; return v58_state } v58_inc() v58_inc() let v58 = v58_inc() // [closure_counter] enum S126_E3 { LOW = 0, MID = 50, HIGH = 100 } local v59 = "" if (v51 < S126_E3.MID) v59 = "low" else if (v51 < S126_E3.HIGH) v59 = "mid" else v59 = "high" // [enum_compare_chain] let v60 = [v35, v17, true, null] // [array_mixed_types] let v61 = v60.len() // [array_len] local v62_state = {scores = [v51, -4294967296, 2], tag = "test"} v62_state.scores[0] += -65536 v62_state.scores[2] = v62_state.scores[0] + v62_state.scores[1] let v62 = v62_state.scores.reduce(@(a, v) a + v, 0) // [table_slot_array_modify] let v63_fn = @[pure, nodiscard] (a, b) a + b let v63 = v63_fn(v2, v36) // [func_pure_nodiscard] let v64 = $"{v19}{v59}" // [string_concat] let v65 = v29 ? v58 : v40 // [ternary_int] enum S126_E4 { LOW = 0, MID = 1, HIGH = 2 } let v66 = (v30 & 3) == S126_E4.MID // [enum_compare] let v67_fn = function(p0, p1, p2) { if (v60.len() > 0) v60[0] = v32 let u0 = v60?[0] ?? 4294967296 // [array_set_elem] let u1 = v60.hasvalue(v57) // [array_hasvalue] let u2_fn = function() { let w0 = v48.len() > 4 ? v48.slice(1, 3) : v48 // [string_slice_both] let w1 = freeze(clone v46) // [freeze_table] let w2 = v29 ? v34 : v57 // [ternary_int] return w2 } let u2 = u2_fn() // [nested_func depth=2] let u3 = array(v62 & 7, -65536) // [array_function_init] return u2 } let v67 = v67_fn(v4, v45, v53) // [nested_func depth=1] let v68 = v46.len() > 0 ? v46.reduce(@(a, _v) a) : -2147483649 // [table_reduce] let v69 = typeof v60 == "array" // [typeof_array_check] function [pure, nodiscard] v70_fn(x) { return x - 0x1 } let v70 = v70_fn(v26) // [named_func_pure_nodiscard] local v71 = 0 foreach (_k, _v in v46) { v71++ } // [foreach_table_keys] let v72 = v46.values() // [table_values] local v73 = 0 for (local v73_i = 0; v73_i < 1; v73_i++) { if (v73_i % 2 == 0) continue v73 = v73 + v2 } // [for_continue] let v74 = (@(x) -x)(v50) // [lambda_negate] let v75_fn = function() { let u0 = [v1, v73] // [array_of_ints] let u1_fn = function(...) { local s = 0 foreach (v in vargv) s += v return s } let u1 = u1_fn(v44, v74, -4294967297) // [vargv_sum] let u2_fn = function(q0) { local w0 = 0 foreach (c in v3) { if (c >= 'A' && c <= 'Z') w0++ } // [string_scan_upper] let w1 = (true ? (v55 ? v23 : v23) : v59) // [string_literal] let w2 = [] local w2_i = 0 while (w2_i < 0) { w2.append(w2_i * w2_i) w2_i++ } // [while_array_fill] let w3 = v63.tofloat() // [int_to_float] local w4 = 0 foreach (_c in v59) w4++ // [foreach_string_chars] let w5 = v46?.x ?? 256 // [nullsafe_field] return w5 } let u2 = u2_fn(v56) // [nested_func depth=2] let u3_t = {x = v35, y = 0xDEADBEEF} let u3_v = u3_t.rawget("x") ?? -65536 u3_t.rawdelete("x") let u3 = u3_v // [table_rawdelete] local u4 = "" if (let u4_v = v64; u4_v.len() > 0) { u4 = u4_v.toupper() } // [if_let_string] const u5 = 1e-40 // [const_float] return u5 } let v75 = v75_fn() // [nested_func depth=1] let v76 = v32 < v30 ? v32 : v30 // [int_min_two] local v77: string = v59 // [typed_string_var] let v78_fn = function(p0, p1, p2) { let u0 = v45 > 0 ? 1 : (v45 < 0 ? -1 : 0) // [int_sign] let u1_t = freeze({x = v24, y = -4}) let u1 = u1_t.x + u1_t.y // [freeze_nested] local u2_val = v34 let u2_set = function(x) { u2_val = x } let u2_get = function() { return u2_val } u2_set(v34 + -255) let u2 = u2_get() // [closure_getter_setter] let u3 = v41 assert(u3 == u3) // [assert_tautology] let u4 = v75.tointeger() // [float_to_int] return u4 } let v78 = v78_fn(v42, v10, v6) // [nested_func depth=1] let v79 = v46.len() // [table_len] let v80 = clone v72 for (local v80_i = 0; v80_i < v80.len(); v80_i++) v80[v80_i] = v49 // [array_modify_elems] local v81 = -257 if (let v81_v = v46?.x; v81_v != null) { v81 = v81_v } // [if_let_nullcheck] let v82_fn = function(a, b, combine = {op = @(x, y) x + y, identity = 257}) { return combine.op(a, b) + combine.identity } let v82_add = v82_fn(v26, v63) let v82_mul = v82_fn(v26, v63, {op = @(x, y) x * y, identity = -4294967297}) let v82 = v82_add + v82_mul // [default_param_fn_field_call] local v83_cfg = {a = {val = v25 + 32769}, b = {val = v25 - 0x8000}} let v83 = v66 ? (v83_cfg?.a?.val ?? 0x100) : (v83_cfg?.b?.val ?? 0x8000) // [conditional_deep_path] let v84_fn = function(p0) { local u0 = -256 u0 = v40 u0 = u0 + 2147483649 u0 = u0 * 3 // [expr_stmt_assign_chain] let u1 = v4 || v71 // [logical_or_val] let u2 = v43 != 0 ? v58 % v43 : 0x7FFF // [int_mod_safe] return u2 } let v84 = v84_fn(v57) // [nested_func depth=1] let v85_t = clone v46 v85_t.tmp <- -2147483648 let v85 = v85_t.tmp // [table_delete_read] let v86 = typeof null == "null" // [typeof_null_check] let v87_v = v39 & 0xFFFF let v87 = v87_v > 0 && (v87_v & (v87_v - 1)) == 0 // [int_pow2_check] let v88_fn = function([da, db = -4]) { return da * db } let v88 = v88_fn([v45]) // [destruct_param_array_default] let v89_arr = v46?.keys let v89 = v89_arr != null ? v46.keys().len() : -4294967298 // [nullsafe_method_on_result] let v90 = [v86, v66, true, false] // [array_of_bools] let v91 = v3 == v48 // [str_cmp_eq] let v92_fn = function(p0, p1) { let u0 = v46.keys() // [table_keys] local u1 = 3 try { let g = (function() { yield v34; yield v34 + 1 })() u1 = resume g } catch (_e) { u1 = -2147483648 } // [coroutine_try_catch] local u2 = v9 u2 *= v25 // [modify_mul_int] let u3 = [] // [empty_array] return u2 } let v92 = v92_fn(v5, v35) // [nested_func depth=1] let v93 = v90.len() >= 2 ? v90[0] + v90[1] : v90.len() == 1 ? v90[0] : -65535 // [array_two_elems] let v94_fn = function(x: int|float): int { return x.tointeger() } let v94 = v94_fn(v38) // [typed_union_param] let v95 = (function(x) { return x * x })(v39) // [immediate_invoke] local v96 = v18 v96 %= v13 // [modify_mod_int_unsafe] let v97 = [65535, 129, 4294967295] v97.insert(1, v38) v97.remove(0) // [array_insert_remove] local v98 = v77 v98 += v17 // [modify_string_concat] let v99_a = [v26, v26 + 1] v99_a.append(v26 * 2) let v99 = v99_a.pop() // [array_push_pop] local v100 = "" local v100_i = 0 while (v100_i < 1) { v100 = v100 + v52 v100_i++ } // [while_string_build] let v101 = v87 ? v100 : v17 // [ternary_str] let v102_fn = function(p0, p1, p2) { let u0 = [v18, v34] // [array_of_ints] let u1 = v19 < p0 // [string_cmp_lt] let u2_t = clone v46 u2_t.clear() let u2 = u2_t.len() // [table_clear] let u3_fn = function(a, b = "test") { return a + b } let u3 = u3_fn(p0) // [default_param_string] let u4 = v46.map(@(v) v) // [table_map] const u5 = "\n" // [const_string] return u5 } let v102 = v102_fn(v52, v57, v42) // [nested_func depth=1] local v103 = 0 foreach (c in v48) { if (c >= 'A' && c <= 'Z') v103++ } // [string_scan_upper] let v104_fn = function(p0) { let u0_Base = class { function compute(x) { return x + -129 } } let u0_Der = class(u0_Base) { function compute(x) { return base.compute(x) * 32767 } } let u0 = u0_Der().compute(v16) // [class_method_override] let u1 = -v75 // [float_neg] let u2_fn = function(q0, q1) { local w0 = "" foreach (c in v98) { if (c != ' ') w0 += c.tochar() } // [string_build_filtered] let w1_f = @(x) x * -4294967296 let w1_g = @(x) x + 'A' let w1 = w1_f(w1_g(v68)) // [func_compose] let w2_fn = function(a, b = "0") { return a + b } let w2 = w2_fn(v101) // [default_param_string] let w3_C = class { xv = 0 constructor(x) { this.xv = x } function _sub(o) { return this.getclass()(this.xv - o.xv) } } let w3_r = w3_C(v51) - w3_C(v70) let w3 = w3_r.xv // [meta_sub] let w4_fn = function(r0, r1, r2) { let z0 = v6 && v66 // [bool_and] let z1_Base = class { xv = 0 } let z1_Der = class(z1_Base) { yv = 0 } let z1 = z1_Der.getbase() == z1_Base // [class_getbase] let z2 = [-2147483648, -4294967297, 0x7FFF] z2.insert(1, v8) z2.remove(0) // [array_insert_remove] local z3: float = u1 // [typed_float_var] return z3 } let w4 = w4_fn(v79, v42, u1) // [nested_func depth=3] return w4 } let u2 = u2_fn(v42, v15) // [nested_func depth=2] let u3_fn = function(q0, q1) { const w0_BASE = 256 const w0_SCALE = -65536 let w0 = v24 * w0_SCALE + w0_BASE // [const_in_expr] let w1 = v80.map(@(v) v) // [array_map_lambda] let w2 = v80.filter(@(v) v != null) // [array_filter_lambda] print($"array.len={w2.len()}\n") // [print_array] local w3_t = {arr = [9223372036854775807, null, {key = v88}]} local w3_inner = w3_t?.arr?[2] if (w3_inner != null) w3_inner.key += 257 let w3 = w3_t?.arr?[2]?.key ?? 1 // [nested_table_array_modify] let w4_fn = function() { let z0 = typeof u2 == "float" // [typeof_float_check] let z1 = v17.lstrip() // [string_lstrip] function [pure] z2_fn(x) { return x * 9223372036854775807 } let z2 = z2_fn(v84) // [named_func_pure] return z2 } let w4 = w4_fn() // [nested_func depth=3] return w4 } let u3 = u3_fn(v89, v37) // [nested_func depth=2] return u3 } let v104 = v104_fn(v34) // [nested_func depth=1] let v105 = v101.replace("\u0000", "\"") // [string_replace] let v106_inner = function(...) { local s = 0 foreach (v in vargv) s += v return s } let v106_outer = function(a, ...) { return a + v106_inner(vargv.len(), a) } let v106 = v106_outer(v15, v32) // [vargv_forward] let v107_C = class { _val = 0 constructor(xv) { this._val = xv } function getVal() { return this._val } } let v107_obj = v107_C(v20) let v107 = v107_obj.getVal() // [class_basic] let v108_fn = function({da, db = -130}) { return da + db } let v108 = v108_fn({da = v93}) // [destruct_param_table_default] let v109_fn = function(a, b = 3, c = '\'') { return a + b + c } let v109 = v109_fn(v67) // [default_param_multi] let v110_fn = function(a, b = 2) { return a + b } let v110 = v110_fn(v44, v45) // [default_param_call_with] let v111 = (@(a, b) a + b)(v10, v45) // [immediate_lambda] let v112 = freeze(clone v80) // [freeze_array] let v113 = v64.escape() // [string_escape] print($"int={v110}\n") // [print_int] const v114 = 3 // [const_int] let v115 = type(v106) // [type_function] let [v116_a, v116_b] = [v18, v15] let [v116_x, v116_y] = [v116_b, v116_a] let v116 = v116_x + v116_y // [destruct_swap] let v117 = v69 || v66 // [bool_or] let v118_fn = function(p0) { let u0 = v48.startswith(v98) // [string_startswith] let u1_q = [v111, v2] u1_q.append(0xFFFF) let u1_val = u1_q[0] u1_q.remove(0) let u1 = u1_val // [array_as_queue] let u2 = $"val={v75}" // [string_interp_float] return u2 } let v118 = v118_fn(v37) // [nested_func depth=1] let v119 = {x = v57, s = v23} // [table_create] let v120 = {[v115] = v31, ["y"] = -2147483648} // [table_computed_key] let v121_C = class { xv = 0 constructor(x) { this.xv = x } } let v121_arr = [v121_C(v70), v121_C(v70 + 1), v121_C(v70 + 2)] let v121 = v121_arr[0].xv + v121_arr[1].xv + v121_arr[2].xv // [class_array_of] let v122 = v90.hasvalue(v37) // [array_hasvalue] let v123_fn = function() { local u0 = 0 foreach (c in v118) { if (c >= 'A' && c <= 'Z') u0++ } // [string_scan_upper] let u1 = ~v7 // [int_bitnot] let u2_fn = function(q0, q1, q2) { let w0 = {} // [empty_table] let w1_fn = function(n) { local i = 0 while (i < 7) { if (i * n > 0x1) return i i++ } return i } let w1 = w1_fn(v107) // [while_early_return] local w2 = v54 & 7 do { w2-- } while (w2 > 0) // [do_while_dec] let w3_t = {[v48] = v4} let w3 = w3_t?[v48] ?? -2147483649 // [table_computed_key_read] let w4_fn = function() { local z0_val = v94 let z0_set = function(x) { z0_val = x } let z0_get = function() { return z0_val } z0_set(v94 + 32767) let z0 = z0_get() // [closure_getter_setter] local z1 = v109 z1 -= v45 // [modify_sub_int] let z2 = null // [null_var] let z3 = [v104, v103] // [array_of_ints] return z2 } let w4 = w4_fn() // [nested_func depth=3] return w4 } let u2 = u2_fn(v49, v29, v25) // [nested_func depth=2] print($"float={v75}\n") // [print_float] local u3 = 0 local u3_v = v65 & 0xFF while (u3_v != 0) { u3 += u3_v & 1 u3_v = u3_v >>> 1 } // [int_bit_count] return u3 } let v123 = v123_fn() // [nested_func depth=1] let v124_t = {arr = [4294967296, null, {key = v10}]} let v124 = v124_t?.arr?[2]?.key ?? -2147483647 // [nested_table_array_nullsafe_read] let v125 = (false ? (((-255) <= 65537) ? true : true) : v87) // [bool_literal] local v126 = "" foreach (c in v115) { if (c != ' ') v126 += c.tochar() } // [string_build_filtered] let v127_fn = function(p0, p1) { let u0_fn = @[nodiscard] (x) x + 4294967297 let u0 = u0_fn(v82) // [func_nodiscard] let u1 = v3.tolower() // [string_tolower] let u2_C = class { xv = 0 extra = 0 constructor(x) { this.xv = x } function _cloned(_other) { this.extra = -4294967295 } } let u2_orig = u2_C(v47) let u2_cpy = clone u2_orig let u2 = u2_cpy.xv + u2_cpy.extra // [meta_cloned] let u3_fn = function(q0) { local w0 = v76 --w0 // [prefix_dec] local w1 = typeof v114 switch (w1) { case "integer": w1 = "int"; break case "float": w1 = "flt"; break case "string": w1 = "str"; break default: w1 = "other" } // [typeof_switch] let w2_C = class { v = 0 constructor(x) { this.v = x } function get() { return this.v } } local w2_holder = {obj = w2_C(v45), tag = "\t\n\r"} let w2 = w2_holder?.obj?.get() ?? 0xFFFF // [table_with_instance_field] let w3 = (1e-37).tointeger() // [int_literal] let w4 = v90.len() >= 2 ? v90[0] + v90[1] : v90.len() == 1 ? v90[0] : 32767 // [array_two_elems] return w4 } let u3 = u3_fn(v79) // [nested_func depth=2] let u4_fn = function() { let w0 = [] v119.each(@(v, _k) w0.append(v)) // [table_each_collect] let w1_fn = @[nodiscard] (x) x + 32769 let w1 = w1_fn(v116) // [func_nodiscard] let w2_fn = function(n) { local i = 0 while (i < 3) { if (i * n > -32767) return i i++ } return i } let w2 = w2_fn(v54) // [while_early_return] let w3_fn = @[pure, nodiscard] (a, b) a + b let w3 = w3_fn(v16, v28) // [func_pure_nodiscard] let w4_fn = function(r0, r1) { let z0 = array(v111 & 7, -9223372036854775807) // [array_function_init] let z1_a = [v79, v79 + 1] z1_a.append(v79 * 2) let z1 = z1_a.pop() // [array_push_pop] local z2_db = {users = {alice = {score = 65535, level = 32769}, bob = {score = 4294967295, level = -257}}} z2_db.users.alice.score += v94 z2_db.users.bob.score -= v94 let z2 = (z2_db?.users?.alice?.score ?? 128) + (z2_db?.users?.bob?.score ?? -65535) // [deep_nested_db_modify] let z3 = [] local z3_i = 0 while (z3_i < -1) { z3.append(z3_i * z3_i) z3_i++ } // [while_array_fill] return z2 } let w4 = w4_fn(v32, v96) // [nested_func depth=3] let w5 = v119?.x ?? -129 // [null_coalesce] return w5 } let u4 = u4_fn() // [nested_func depth=2] let u5 = v120.values() // [table_values] return u4 } let v127 = v127_fn(v28, v108) // [nested_func depth=1] local v128_mat = [[2147483647, -32768, 1000], [-127, 0xFFFF, -256]] v128_mat[0][1] += v24 v128_mat[1][0] -= v24 let v128 = (v128_mat?[0]?[1] ?? 0) + (v128_mat?[1]?[0] ?? -127) // [matrix_modify_read] let v129_C = class { xv = 0 constructor(x) { this.xv = x } function _mul(o) { return this.getclass()(this.xv * o.xv) } } let v129_r = v129_C(v88) * v129_C(v67) let v129 = v129_r.xv // [meta_mul] let v130 = v14 >>> -1 // [int_ushr] let v131 = v72.len() > 0 ? v72.reduce(@(a, _b) a) : 2 // [array_reduce_lambda] let v132 = clone v60 if (v132.len() >= 2) v132.swap(0, v132.len() - 1) // [array_swap] let v133 = v97.hasvalue(v30) // [array_hasvalue] let v134 = v64.tolower() // [string_tolower] let v135_fn = function(p0, p1, p2) { let u0 = v93 != v81 // [int_cmp_ne] let u1 = (("\"" + v19)).toupper() // [string_literal] const function [pure] u2_fn(a, b) { return a + b * -2147483647 } const u2 = u2_fn(32767, 32767) // [const_pure_call_multi] let u3_fn = function(q0) { let w0 = ((v125 || q0) ? v126 : ("he said \"hi\"").toupper()) // [string_literal] enum S126_E5 { A, B, C } let w1 = S126_E5.A + S126_E5.B + S126_E5.C // [enum_basic] let w2 = clone v120 w2.__update(v119) // [table_update] local w3_a = v107 local w3_b = v5 w3_a = w3_a ^ w3_b w3_b = w3_a ^ w3_b w3_a = w3_a ^ w3_b let w3 = w3_a // [int_swap_via_xor] let w4_fn = function(r0, r1, r2) { local z0_maybe = v25 > -32770 ? v25 : null let z0 = z0_maybe ?? 4294967295 // [null_assign_fallback] let z1 = (w2.findindex(@(_v) true) ?? "\u0000").tostring() // [table_findindex] let z2 = u2.tofloat() + v75 // [int_float_arith] let z3_C = class { xv = 0 constructor(x) { this.xv = x } function _add(o) { return this.getclass()(this.xv + o.xv) } } let z3_r = z3_C(v73) + z3_C(v93) let z3 = z3_r.xv // [meta_add] return z3 } let w4 = w4_fn(v53, v96, v36) // [nested_func depth=3] return w4 } let u3 = u3_fn(v125) // [nested_func depth=2] let u4 = ((v67 >>> 1)).tostring() // [string_literal] let u5 = "{0}+{1}".subst(v59, "[]{}()") // [string_subst] local u6_store = {pages = [{lines = [4294967295, 0xFFFFFFFF]}, {lines = [65535, 2147483647]}]} u6_store.pages[0].lines[1] += u2 let u6 = u6_store?.pages?[0]?.lines?[1] ?? -32768 // [nested_db_array_field] return u6 } let v135 = v135_fn(v62, v91, v16) // [nested_func depth=1] local v136 = 0 foreach (v in v90) { if (typeof v == "integer") v136 = v136 + v } // [foreach_array_sum] let v137 = -v75 // [float_neg] const v138 = -1e+37 // [const_float] let v139 = v119.__merge(v120) // [table_merge] local v140 = "" foreach (c in v105) { if (c != ' ') v140 += c.tochar() } // [string_build_filtered] let v141_fn = function(x) { local cached = static({v = -2147483647 * 4294967296}) return x + cached.v } let v141 = v141_fn(v114) // [static_complex] let v142 = v97.totable() // [array_totable] let v143_fn = function(x, s, cfg = { fmt = @(n, t) $"{n}:{t}", limits = {lo = -1, hi = -32770} }) { local clamped = x < cfg.limits.lo ? cfg.limits.lo : x > cfg.limits.hi ? cfg.limits.hi : x return cfg.fmt(clamped, s) } let v143 = v143_fn(v85, v118) // [default_param_config_nested] let v144_fn = function(p0) { let u0 = v97.totable() // [array_totable] local u1 = -9223372036854775807 switch (v77) { case "a": u1 = -4; break case "b": u1 = 257; break case "c": u1 = -2147483648; break default: u1 = 0 } // [switch_string] let u2 = (function() { let gen = (function() { yield -257 yield -257 yield -4 })() let arr = [] while (gen.getstatus() == "suspended") { arr.append(resume gen) } return arr })() // [coroutine_collect_all] return u1 } let v144 = v144_fn(v17) // [nested_func depth=1] let v145 = $"{v19}{v59}" // [string_concat] let v146 = v120.keys() // [table_keys] let v147 = v119.rawget("x") ?? -32770 // [rawget_table] local v148 = v138 v148 *= v75 // [modify_mul_float] let v149_stack = [] v149_stack.append(v58) v149_stack.append(v54) let v149 = v149_stack.pop() // [table_as_stack] let v150_t = {arr = [0x8000, null, {key = v76}]} let v150 = v150_t?.arr?[2]?.key ?? 65535 // [nested_table_array_nullsafe_read] let v151 = v72.hasindex(0) // [array_hasindex] let v152 = v46.topairs() // [table_topairs] let v153 = v117 && v11 // [bool_and] let v154_fn = @(a: int, b: int) a * b let v154 = v154_fn(v2, v67) // [typed_lambda] let v155 = v97.totable() // [array_totable] let v156 = v5 >= v99 // [int_cmp_ge] let v157_fn = function(x) { local cached = static({v = -4294967296 * 2147483648}) return x + cached.v } let v157 = v157_fn(v9) // [static_complex] enum S126_E6 { LOW = 0, MID = 1, HIGH = 2 } let v158 = (v79 & 3) == S126_E6.MID // [enum_compare] let v159 = {} // [empty_table] let v160 = freeze(clone v72) // [freeze_array] let v161 = v155.map(@(v) v) // [table_map] let v162 = v115.toupper() // [string_toupper] let v163_fn = function(a, b = "true") { return a + b } let v163 = v163_fn(v145) // [default_param_string] local v164 = -127 if (let v164_v = v31; v164_v > 0xFFFF) { v164 = v164_v * -255 } // [if_let_positive] let v165_fn = function() { let u0 = v161.filter(@(v) v != null) // [table_filter] let u1 = u0.len() > 0 // [table_getdelegate] let u2_fn = function(q0, q1, q2) { local w0 = 0 for (local w0_i = 0; w0_i < 1; w0_i++) { for (local w0_j = 0; w0_j < -4; w0_j++) { w0 += w0_i * w0_j } } // [nested_loops] const function [pure] w1_fn(a, b) { return a + b * 2147483648 } const w1 = w1_fn(-4294967298, 42) // [const_pure_call_multi] let w2 = v34 assert(w2 == w2) // [assert_tautology] local w3 = v128 w3 /= v38 // [modify_div_int_unsafe] let w4_t = {a = {b = {c = v45}}} let w4 = w4_t?.a?.b?.c ?? 0x8000 // [table_deep] return w4 } let u2 = u2_fn(v117, v78, v89) // [nested_func depth=2] return u2 } let v165 = v165_fn() // [nested_func depth=1] let v166 = v28 > 0 ? 1 : (v28 < 0 ? -1 : 0) // [int_sign] let v167 = typeof v119 == "table" // [typeof_table_check] let v168_fn = function(p0, p1, p2) { local u0 = 0 local u0_i = 0 while (u0_i < 100) { if (u0_i >= 5) break u0 = u0 + v37 u0_i++ } // [while_break] let u1_prev = v161?.x ?? 2147483649 v161.x <- v53 let u1 = u1_prev + v161.x // [table_modify_field] let u2 = v106 == v83 // [int_cmp_eq] return u2 } let v168 = v168_fn(v42, v87, v106) // [nested_func depth=1] let v169 = freeze(clone v112) // [freeze_array] let v170 = v142.map(@(v) v) // [table_map] let v171 = v170.len() > 0 ? v170.reduce(@(a, _v) a) : 129 // [table_reduce] let v172_A = class { v = 0; constructor(x) { this.v = x }; function get() { return this.v } } let v172_B = class(v172_A) { bonus = 0; constructor(x) { base.constructor(x); this.bonus = -4294967297 } } let v172_C = class(v172_B) { extra = 0; constructor(x) { base.constructor(x); this.extra = -3 } } let v172_obj = v172_C(v121) let v172 = v172_obj.get() + v172_obj.bonus + v172_obj.extra // [class_three_level] local v173 = 0 v46.each(@(_v, _k) v173++) // [table_each] let v174 = [] for (local v174_i = 0; v174_i < -4; v174_i++) { v174.append(v174_i) } // [for_build_array] let v175 = v21 - v9 // [int_sub] let v176 = v90.len() >= 2 ? v90.slice(1) : clone v90 // [array_slice_mid] let v177 = [v77, v23, @"raw_string"] // [array_of_strings] let v178_fn = function(p0, p1) { let u0 = v0 != 0 ? v135 / v0 : 42 // [int_div_safe] let u1 = $"flag={v133}" // [string_interp_bool] local u2_items = [{k = "world", v = -2}, {k = "", v = 2147483648}, {k = "1e10", v = v14}] foreach (item in u2_items) if (item.v > -258) item.v *= -128 local u2 = 0 foreach (item in u2_items) u2 += item.v // [foreach_array_tables_modify] return u2 } let v178 = v178_fn(v77, v95) // [nested_func depth=1] let v179 = (@(x) x + 4294967296)(v1) // [lambda_inc] let v180 = v59.tolower() // [string_tolower] let v181_C = class { _a = 0 _b = 0 constructor(xa, xb) { this._a = xa; this._b = xb } function sum() { return this._a + this._b } function diff() { return this._a - this._b } } let v181_obj = v181_C(v141, v22) let v181 = v181_obj.sum() + v181_obj.diff() // [class_method_call] local v182 = 1 local v182_i = 0 while (v182_i < 5) { v182 = v182 * ((v54 % 4) + 1) v182_i++ } // [while_mul] local v183 = 0 foreach (v in v132) { if (typeof v == "integer") v183 = v183 + v } // [foreach_array_sum] let v184_fn = function(p0, p1) { local u0_mat = [[-3, -128, -1], [42, 1000, '\t']] u0_mat[0][1] += v131 u0_mat[1][0] -= v131 let u0 = (u0_mat?[0]?[1] ?? 32767) + (u0_mat?[1]?[0] ?? -65535) // [matrix_modify_read] let u1 = v102.len() > 2 ? v102.slice(-2) : v102 // [string_slice_neg] local u2 = -258 if (local u2_v = v53 & 0xFF; u2_v > 0x80000000) { u2_v = u2_v - -129 u2 = u2_v } // [if_local_modify] let u3 = v91.tostring() // [bool_to_string] let u4 = v85 != v12 // [int_cmp_ne] return u4 } let v184 = v184_fn(v85, v63) // [nested_func depth=1] let v185 = typeof v162 == "string" // [typeof_string_check] let v186_C = class { xv = 0 constructor(x) { this.xv = x } function _sub(o) { return this.getclass()(this.xv - o.xv) } } let v186_r = v186_C(v41) - v186_C(v4) let v186 = v186_r.xv // [meta_sub] local v187 = v79 v187 += v57 // [modify_add_int] let v188 = [v183, v150] // [array_of_ints] local v189 = 1 local v189_i = 0 while (v189_i < 1) { v189 = v189 * ((v13 % 4) + 1) v189_i++ } // [while_mul] local v190 = 0 foreach (_k, _v in v142) { v190++ } // [foreach_table_keys] let v191 = v5 - v71 // [int_sub] let v192 = type(v53) // [type_function] let v193_t = {[v180] = v28} let v193 = v193_t?[v180] ?? 2147483648 // [table_computed_key_read] let v194_fn = function(...) { return vargv.len() > 0 ? vargv[0] : -2147483648 } let v194 = v194_fn(v121) // [vargv_first] local v195 = v75 v195 += v148 // [modify_add_float] local v196 = -65535 if (let v196_v = v96; v196_v > 257) { v196 = v196_v * 2 } // [if_let_positive] let v197_fn = function(p0) { let u0 = v27 >= 0 ? v27 : -v27 // [int_abs_ternary] local u1 = 0 local u1_i = 0 do { u1 += v193 u1_i++ } while (u1_i < 3) // [do_while_count] let u2 = v152.len() > 0 ? v152.reduce(@(acc, v) (typeof v == "integer" ? acc + v : acc), 0) : -130 // [array_reduce_sum] print($"int={v136}\n") // [print_int] return u2 } let v197 = v197_fn(v52) // [nested_func depth=1] const v198_BASE = 0x100 const v198_SCALE = 2 let v198 = v21 * v198_SCALE + v198_BASE // [const_in_expr] let v199 = typeof v89 // [typeof_expr] let v200_fn = function(p0, p1) { local u0 = 0 foreach (c in v163) { if (c >= '0' && c <= '9') u0++ } // [string_scan_digits] v46.x <- v171 let u1 = v46.x // [table_set_field] let u2_fn = function(q0, q1) { let w0 = v169.hasvalue(v82) // [array_hasvalue] let w1_fn = function(a, b = ";") { return a + b } let w1 = w1_fn(v23) // [default_param_string] global enum S126_E7 { LO = -4294967298, HI = 3 } let w2 = v89 + S126_E7.LO + S126_E7.HI // [enum_in_expr] local w3 = v96 w3 /= v7 // [modify_div_int_unsafe] local w4 = 0 local w4_i = 0 while (w4_i < 1) { w4 = w4 + v38 w4_i++ } // [while_sum] return w4 } let u2 = u2_fn(v7, v122) // [nested_func depth=2] let u3_fn = @(a, b = -2147483648, c = -65535) a * b + c let u3 = u3_fn(u1) // [default_param_lambda] return u3 } let v200 = v200_fn(v190, v8) // [nested_func depth=1] local v201 = v24 --v201 // [prefix_dec] let v202_C = class { _n = 0 constructor(n) { this._n = n & 7 } function _nexti(idx) { local next = idx == null ? 0 : idx + 1 return next < this._n ? next : null } function _get(idx) { return idx * -65536 } } let v202_obj = v202_C(v147) local v202 = 0 foreach (_i, v in v202_obj) v202 += v // [meta_nexti] let v203_Base = class { xv = 0; constructor(x) { this.xv = x } } let v203_Der = class(v203_Base) { constructor(x) { base.constructor(x) } } let v203_obj = v203_Der(v182) let v203 = v203_obj instanceof v203_Base // [class_instanceof_base] // --- dump pool --- function _prn_t(_t) { let _a = []; _t.each(function(_k, _v) { _a.append(_sv(_k)); _a.append(_sv(_v)) }); _a.sort(); foreach (i, _v in _a) { if (i >= 10) { print("..."); break }; if (i > 0) print(", "); print(_v) }; } function _prn_a(_a) { foreach (i, _v in _a) { if (i >= 10) { print("..."); break }; if (i > 0) print(", "); print(_v) }; } print($"v0={v0}\n") print($"v1={v1}\n") print($"v2={v2}\n") print($"v3={v3}\n") print($"v4={v4}\n") print($"v5={v5}\n") print($"v6={v6}\n") print($"v7={v7}\n") print($"v8={v8}\n") print($"v9={v9}\n") print($"v10={v10}\n") print($"v11={v11}\n") print($"v12={v12}\n") print($"v13={v13}\n") print($"v14={v14}\n") print($"v15={v15}\n") print($"v16={v16}\n") print($"v17={v17}\n") print($"v18={v18}\n") print($"v19={v19}\n") print($"v20={v20}\n") print($"v21={v21}\n") print($"v22={v22}\n") print($"v23={v23}\n") print($"v24={v24}\n") print($"v25={v25}\n") print($"v26={v26}\n") print($"v27={v27}\n") print($"v28={v28}\n") print($"v29={v29}\n") print($"v30={v30}\n") print($"v31={v31}\n") print($"v32={v32}\n") print($"v33={v33}\n") print($"v34={v34}\n") print($"v35={v35}\n") print($"v36={v36}\n") print($"v37={v37}\n") print($"v38={v38}\n") print($"v39={v39}\n") print($"v40={v40}\n") print($"v41={v41}\n") print($"v42={v42}\n") print($"v43={v43}\n") print($"v44={v44}\n") print($"v45={v45}\n") print("v46={"); _prn_t(v46); print("}\n") print($"v47={v47}\n") print($"v48={v48}\n") print($"v49={v49}\n") print($"v50={v50}\n") print($"v51={v51}\n") print($"v52={v52}\n") print($"v53={v53}\n") print($"v54={v54}\n") print($"v55={v55}\n") print($"v56={v56}\n") print($"v57={v57}\n") print($"v58={v58}\n") print($"v59={v59}\n") print("v60=["); _prn_a(v60); print("]\n") print($"v61={v61}\n") print($"v62={v62}\n") print($"v63={v63}\n") print($"v64={v64}\n") print($"v65={v65}\n") print($"v66={v66}\n") print($"v67={v67}\n") print($"v68={v68}\n") print($"v69={v69}\n") print($"v70={v70}\n") print($"v71={v71}\n") print("v72=["); _prn_a(v72); print("]\n") print($"v73={v73}\n") print($"v74={v74}\n") print($"v75={v75}\n") print($"v76={v76}\n") print($"v77={v77}\n") print($"v78={v78}\n") print($"v79={v79}\n") print("v80=["); _prn_a(v80); print("]\n") print($"v81={v81}\n") print($"v82={v82}\n") print($"v83={v83}\n") print($"v84={v84}\n") print($"v85={v85}\n") print($"v86={v86}\n") print($"v87={v87}\n") print($"v88={v88}\n") print($"v89={v89}\n") print("v90=["); _prn_a(v90); print("]\n") print($"v91={v91}\n") print($"v92={v92}\n") print($"v93={v93}\n") print($"v94={v94}\n") print($"v95={v95}\n") print($"v96={v96}\n") print("v97=["); _prn_a(v97); print("]\n") print($"v98={v98}\n") print($"v99={v99}\n") print($"v100={v100}\n") print($"v101={v101}\n") print($"v102={v102}\n") print($"v103={v103}\n") print($"v104={v104}\n") print($"v105={v105}\n") print($"v106={v106}\n") print($"v107={v107}\n") print($"v108={v108}\n") print($"v109={v109}\n") print($"v110={v110}\n") print($"v111={v111}\n") print("v112=["); _prn_a(v112); print("]\n") print($"v113={v113}\n") print($"v114={v114}\n") print($"v115={v115}\n") print($"v116={v116}\n") print($"v117={v117}\n") print($"v118={v118}\n") print("v119={"); _prn_t(v119); print("}\n") print("v120={"); _prn_t(v120); print("}\n") print($"v121={v121}\n") print($"v122={v122}\n") print($"v123={v123}\n") print($"v124={v124}\n") print($"v125={v125}\n") print($"v126={v126}\n") print($"v127={v127}\n") print($"v128={v128}\n") print($"v129={v129}\n") print($"v130={v130}\n") print($"v131={v131}\n") print("v132=["); _prn_a(v132); print("]\n") print($"v133={v133}\n") print($"v134={v134}\n") print($"v135={v135}\n") print($"v136={v136}\n") print($"v137={v137}\n") print($"v138={v138}\n") print("v139={"); _prn_t(v139); print("}\n") print($"v140={v140}\n") print($"v141={v141}\n") print("v142={"); _prn_t(v142); print("}\n") print($"v143={v143}\n") print($"v144={v144}\n") print($"v145={v145}\n") print("v146=["); _prn_a(v146); print("]\n") print($"v147={v147}\n") print($"v148={v148}\n") print($"v149={v149}\n") print($"v150={v150}\n") print($"v151={v151}\n") print("v152=["); _prn_a(v152); print("]\n") print($"v153={v153}\n") print($"v154={v154}\n") print("v155={"); _prn_t(v155); print("}\n") print($"v156={v156}\n") print($"v157={v157}\n") print($"v158={v158}\n") print("v159={"); _prn_t(v159); print("}\n") print("v160=["); _prn_a(v160); print("]\n") print("v161={"); _prn_t(v161); print("}\n") print($"v162={v162}\n") print($"v163={v163}\n") print($"v164={v164}\n") print($"v165={v165}\n") print($"v166={v166}\n") print($"v167={v167}\n") print($"v168={v168}\n") print("v169=["); _prn_a(v169); print("]\n") print("v170={"); _prn_t(v170); print("}\n") print($"v171={v171}\n") print($"v172={v172}\n") print($"v173={v173}\n") print("v174=["); _prn_a(v174); print("]\n") print($"v175={v175}\n") print("v176=["); _prn_a(v176); print("]\n") print("v177=["); _prn_a(v177); print("]\n") print($"v178={v178}\n") print($"v179={v179}\n") print($"v180={v180}\n") print($"v181={v181}\n") print($"v182={v182}\n") print($"v183={v183}\n") print($"v184={v184}\n") print($"v185={v185}\n") print($"v186={v186}\n") print($"v187={v187}\n") print("v188=["); _prn_a(v188); print("]\n") print($"v189={v189}\n") print($"v190={v190}\n") print($"v191={v191}\n") print($"v192={v192}\n") print($"v193={v193}\n") print($"v194={v194}\n") print($"v195={v195}\n") print($"v196={v196}\n") print($"v197={v197}\n") print($"v198={v198}\n") print($"v199={v199}\n") print($"v200={v200}\n") print($"v201={v201}\n") print($"v202={v202}\n") print($"v203={v203}\n") } catch(e) { print(e) } print("===\n") ================================================ FILE: testData/diagnostics/type_hints_01.diag.txt ================================================ AN ERROR HAS OCCURRED [parameter 1 of 'fn' has an invalid type 'float' ; expected: 'null|integer'] CALLSTACK *FUNCTION [__main__()] testData/diagnostics/type_hints_01.nut:5 LOCALS [fn] CLOSURE=FN:fn [vargv] ARRAY=[] [this] NULL ================================================ FILE: testData/diagnostics/type_hints_01.nut ================================================ function fn(x: int|null) { return x } fn(5.4) ================================================ FILE: testData/diagnostics/type_hints_02.diag.txt ================================================ AN ERROR HAS OCCURRED [parameter 2 of 'fn' has an invalid type 'float' ; expected: 'null|integer'] CALLSTACK *FUNCTION [__main__()] testData/diagnostics/type_hints_02.nut:5 LOCALS [fn] CLOSURE=FN:fn [vargv] ARRAY=[] [this] NULL ================================================ FILE: testData/diagnostics/type_hints_02.nut ================================================ function fn(y, x: int|null) { return x } fn("abc", 5.4) ================================================ FILE: testData/diagnostics/type_hints_03.diag.txt ================================================ AN ERROR HAS OCCURRED [parameter 2 of 'fn' has an invalid type 'float' ; expected: 'null|integer'] CALLSTACK *FUNCTION [__main__()] testData/diagnostics/type_hints_03.nut:5 LOCALS [fn] CLOSURE=FN:fn [vargv] ARRAY=[] [this] NULL ================================================ FILE: testData/diagnostics/type_hints_03.nut ================================================ function fn(y: null|string, x: int|null, z: null|table) { return x } fn(null, 5.4, null) ================================================ FILE: testData/diagnostics/type_hints_04.diag.txt ================================================ AN ERROR HAS OCCURRED [parameter 1 of 'fn' has an invalid type 'string' ; expected: 'integer'] CALLSTACK *FUNCTION [__main__()] testData/diagnostics/type_hints_04.nut:5 LOCALS [fn] CLOSURE=FN:fn [vargv] ARRAY=[] [this] NULL ================================================ FILE: testData/diagnostics/type_hints_04.nut ================================================ function fn(x: int, ...) { return x } fn("abc", "") ================================================ FILE: testData/diagnostics/type_hints_05.diag.txt ================================================ AN ERROR HAS OCCURRED [parameter 5 of 'fn' has an invalid type 'function' ; expected: 'table'] CALLSTACK *FUNCTION [__main__()] testData/diagnostics/type_hints_05.nut:5 LOCALS [fn] CLOSURE=FN:fn [vargv] ARRAY=[] [this] NULL ================================================ FILE: testData/diagnostics/type_hints_05.nut ================================================ function fn(x: int, ...: table) { return x } fn(4, {}, {}, {}, @() null) ================================================ FILE: testData/diagnostics/type_hints_06.diag.txt ================================================ AN ERROR HAS OCCURRED [parameter 2 of 'fn' has an invalid type 'null' ; expected: 'array'] CALLSTACK *FUNCTION [__main__()] testData/diagnostics/type_hints_06.nut:5 LOCALS [fn] CLOSURE=FN:fn [vargv] ARRAY=[] [this] NULL ================================================ FILE: testData/diagnostics/type_hints_06.nut ================================================ function fn(x: int, ...: array) { return x } fn(4, null, [], [], []) ================================================ FILE: testData/diagnostics/unfinished_hex.diag.txt ================================================ ERROR: expected hex digits after '0x' testData/diagnostics/unfinished_hex.nut:1:8 println(0x) ^- ================================================ FILE: testData/diagnostics/unfinished_hex.nut ================================================ println(0x) ================================================ FILE: testData/diagnostics/var_scope_1.diag.txt ================================================ ERROR: Unknown variable [x] testData/diagnostics/var_scope_1.nut:4:6 print(x) ^ ================================================ FILE: testData/diagnostics/var_scope_1.nut ================================================ if (true) local x = 5 print(x) ================================================ FILE: testData/diagnostics/var_scope_2.diag.txt ================================================ ERROR: Unknown variable [x] testData/diagnostics/var_scope_2.nut:5:6 print(x) ^ ================================================ FILE: testData/diagnostics/var_scope_2.nut ================================================ local function f() { local x = 6 } print(x) ================================================ FILE: testData/diagnostics/var_scope_3.diag.txt ================================================ ERROR: Unknown variable [x] testData/diagnostics/var_scope_3.nut:4:8 else print(x) ^ ================================================ FILE: testData/diagnostics/var_scope_3.nut ================================================ if (true) local x = 6 else print(x) ================================================ FILE: testData/exec/array_methods.nut ================================================ let a = [1,2,3] let Exception = class{ e = null constructor(e){ this.e=e } } function test(func, expected_res, name = null){ let name_s = name ? $"'{name}': " : "" try{ let res = func() assert(expected_res==res, @() $"{name_s}Expected {expected_res}, got {res}") println(res) } catch(e){ assert(expected_res instanceof Exception, $"{name_s} expected result '{expected_res}', got Exception('{e}')}") assert(expected_res.e == null || expected_res.e==e, $"{name_s}expected Exception value = '{expected_res.e}', got '{e}'") } } try{ test(@() a.contains(1), true, "a.contains(1)") test(@() a.hasindex(0), true, "a.hasindex(0)") test(@() a.hasindex(5), false) test(@() a.hasindex(-1), false) test(@() a.hasindex(a.len()-1), true, "a.hasindex(a.len()-1)") test(@() a.hasvalue(5), false, "a.hasvalue(5)") test(@() a.hasvalue(3), true) test(@() a.pop(), 3) test(@() a.insert(0,0)[1], 1) test(@() a.remove(0), 0) test(@() a.extend([5,6]).len(), 4) test(@() a.resize(7, -1).top(), -1) test(@() a.reverse().top(), 1) test(@() a.sort().top(), 6, "a.sort().top()") test(@() a.slice(0,3)[2], -1) test(@() a.clear().len(), 0) test(@() a.append(0, 1, 2).indexof(1), 1) test(@() a.findindex(@(v) v==-1), null) test(@() a.findindex(@(v) v%2!=0), 1) test(@() a.findvalue(@(v) v==1), 1) test(@() a.totable()[0], 0) test(@() a.replace_with([-1,-2,-3]).top(), -3) test(@() type(a.tostring())=="string", true) test(@() a.swap(0,2)[0], -3) test(@() [0,1].totable().swap(0,1)[0], 1) print("ok") } catch(e){ println(e) } ================================================ FILE: testData/exec/array_methods.out ================================================ true true false false true false true 3 1 0 4 -1 1 6 -1 0 1 null 1 1 0 -3 true -3 1 ok ================================================ FILE: testData/exec/basics.nut ================================================ //tests for local bindings, local variables and const const A = 2 let a = 1 local b = 3 b = 10 let b_0 = b b += a b -= 1 b++ b-- b = b + A b = b - A b *= 2 b /= 2 b = b * 2 b = b / 2 b = (b + 1) * 2 b = b / 2 -1 assert(b == b_0) ================================================ FILE: testData/exec/basics.out ================================================ ================================================ FILE: testData/exec/call_constructor_recursion.nut ================================================ // Demonstrates SQVM::Call self-recursion via class constructor (1.5) // // When a class is called to create an instance, Call() does two things: // 1. CreateClassInstance() - allocates the instance // 2. Call(constructor, ...) - calls the constructor on the new instance // // sqvm.cpp:2436-2448 (case OT_CLASS): // Call(class) // entry: closure type is OT_CLASS // -> CreateClassInstance(class) // line 2440 - allocates instance // -> Call(constructor, ...) // line 2445 - re-enters Call() with OT_CLOSURE // -> Execute() runs constructor body // // If the constructor body creates another instance of the same class, // Execute() emits _OP_CALL with the class object, which calls Call(class) // again. Each level adds TWO native Call() frames (class + constructor): // // Call(MyClass) -- OT_CLASS dispatch, CreateClassInstance // Call(constructor) -- OT_CLOSURE dispatch -> Execute // Call(MyClass) -- from `this.getclass()()` in constructor body // Call(constructor) // Call(MyClass) // ... // // No depth limit exists in Call() itself. // // RESULT: Unlike _tostring/_get metamethod recursion (which the VM catches // at ~99 deep with "Native stack overflow"), this path causes a HARD CRASH // of the process. The Call(OT_CLASS) branch does extra work per level // (CreateClassInstance + stack manipulation) consuming more native stack // per recursion step, so the process segfaults before the VM's stack // overflow probe (in Execute) gets a chance to fire. // ---- Controlled version: shows the recursion pattern safely ---- local depth = 0 local MAX = 10 class MyClass { child = null constructor() { depth++ if (depth <= 5) println($" Call(MyClass) -> Call(ctor) depth={depth}") // this.getclass() returns the OT_CLASS object // calling it triggers Call(class) -> CreateClassInstance -> Call(ctor) if (depth < MAX) this.child = this.getclass()() } } println($"=== Controlled run (max {MAX}) ===") depth = 0 try { local obj = MyClass() println($"Completed: depth reached {depth}") } catch(e) { println($"Caught after depth={depth}: {e}") } // ---- Unguarded version ---- class B { child = null constructor() { this.child = this.getclass()() } } try { local b = B() } catch(e) { println($"Caught: {e}") // never reached - process is dead } ================================================ FILE: testData/exec/call_constructor_recursion.out ================================================ === Controlled run (max 10) === Call(MyClass) -> Call(ctor) depth=1 Call(MyClass) -> Call(ctor) depth=2 Call(MyClass) -> Call(ctor) depth=3 Call(MyClass) -> Call(ctor) depth=4 Call(MyClass) -> Call(ctor) depth=5 Completed: depth reached 10 Caught: stack overflow, cannot resize stack ================================================ FILE: testData/exec/class_yield.nut ================================================ class A { constructor() { this.call_yield() } function call_yield() { this.do_yield.call(0) } function do_yield() { yield } } A() println("OK") ================================================ FILE: testData/exec/class_yield.out ================================================ OK ================================================ FILE: testData/exec/closure_hoist_typed_default.nut ================================================ // Closure hoisting must not move closures with non-literal typed default // parameters out of try-catch blocks when the default value type is unknown // at compile time. The _OP_CLOSURE instruction checks default value types // at runtime and can throw. // Non-literal default with type mismatch inside try-catch in a nested function. // The closure must not be hoisted because getExprLiteralTypeMask returns ~0u. let wrongVal = vargv.len() < 10000 ? "wrong" : vargv[0] // must have unknown type during compilation let test1 = function() { local result = 0 try { let fn = function(x: int = wrongVal) { return x } } catch(e) { result = 1 } return result } assert(test1() == 1) // Valid typed literal defaults should still allow hoisting and work normally let test2 = function() { let fn = function(x: int = 42) { return x } return fn() } assert(test2() == 42) println("OK") ================================================ FILE: testData/exec/closure_hoist_typed_default.out ================================================ OK ================================================ FILE: testData/exec/closure_hoist_typed_return.nut ================================================ // Verify that scope-aware type deduction for closure hoisting produces // correct runtime behavior: hoisted closures with typed defaults from // const/function return types must work identically to non-hoisted versions. const STR_CONST = "hello" function fnReturnsInt(x): int { return x * x } function fnReturnsStringOrInt(x): string|int { return x ? x : STR_CONST } // Hoisted: const value matches param type let test1 = function() { let fn = function(x: string = STR_CONST) { return x } return fn() } assert(test1() == "hello") // Hoisted: function return type matches param type let test2 = function() { let fn = function(x: int = fnReturnsInt(3)) { return x } return fn() } assert(test2() == 9) // Hoisted: union return type matches union param type let test3 = function() { let fn = function(x: string|int = fnReturnsStringOrInt(0)) { return x } return fn() } assert(test3() == "hello") println("OK") ================================================ FILE: testData/exec/closure_hoist_typed_return.out ================================================ OK ================================================ FILE: testData/exec/compare_int_float.nut ================================================ { // > let a = -1 > -1.0 let b = 5 > -1.0 let c = -1 > 5.0 assert(a == false) assert(b == true) assert(c == false) assert(-1 > -1.0 == false) assert(5 > -1.0 == true) assert(-1 > 5.0 == false) assert((-1 > -1.0 ? 55 : 66) == 66) assert((5 > -1.0 ? 55 : 66) == 55) assert((-1 > 5.0 ? 55 : 66) == 66) if (-1 > -1.0) { assert(false) } else { assert(true) } if (5 > -1.0) { assert(true) } else { assert(false) } if (-1 > 5.0) { assert(false) } else { assert(true) } let x = -1 let y = 5.0 let a1 = x > x let b1 = y > x let c1 = x > y assert(a1 == false) assert(b1 == true) assert(c1 == false) if (x > x) { assert(false) } else { assert(true) } if (y > x) { assert(true) } else { assert(false) } if (x > y) { assert(false) } else { assert(true) } } { // < let a = -1 < -1.0 let b = 5 < -1.0 let c = -1 < 5.0 assert(a == false) assert(b == false) assert(c == true) assert(-1 < -1.0 == false) assert(5 < -1.0 == false) assert(-1 < 5.0 == true) assert((-1 < -1.0 ? 55 : 66) == 66) assert((5 < -1.0 ? 55 : 66) == 66) assert((-1 < 5.0 ? 55 : 66) == 55) if (-1 < -1.0) { assert(false) } else { assert(true) } if (5 < -1.0) { assert(false) } else { assert(true) } if (-1 < 5.0) { assert(true) } else { assert(false) } let x = -1 let y = 5.0 let a1 = x < x let b1 = y < x let c1 = x < y assert(a1 == false) assert(b1 == false) assert(c1 == true) if (x < x) { assert(false) } else { assert(true) } if (y < x) { assert(false) } else { assert(true) } if (x < y) { assert(true) } else { assert(false) } } { // >= let a = -1 >= -1.0 let b = 5 >= -1.0 let c = -1 >= 5.0 assert(a == true) assert(b == true) assert(c == false) assert(-1 >= -1.0 == true) assert(5 >= -1.0 == true) assert(-1 >= 5.0 == false) assert((-1 >= -1.0 ? 55 : 66) == 55) assert((5 >= -1.0 ? 55 : 66) == 55) assert((-1 >= 5.0 ? 55 : 66) == 66) if (-1 >= -1.0) { assert(true) } else { assert(false) } if (5 >= -1.0) { assert(true) } else { assert(false) } if (-1 >= 5.0) { assert(false) } else { assert(true) } let x = -1 let y = 5.0 let a1 = x >= x let b1 = y >= x let c1 = x >= y assert(a1 == true) assert(b1 == true) assert(c1 == false) if (x >= x) { assert(true) } else { assert(false) } if (y >= x) { assert(true) } else { assert(false) } if (x >= y) { assert(false) } else { assert(true) } } { // <= let a = -1 <= -1.0 let b = 5 <= -1.0 let c = -1 <= 5.0 assert(a == true) assert(b == false) assert(c == true) assert(-1 <= -1.0 == true) assert(5 <= -1.0 == false) assert(-1 <= 5.0 == true) assert((-1 <= -1.0 ? 55 : 66) == 55) assert((5 <= -1.0 ? 55 : 66) == 66) assert((-1 <= 5.0 ? 55 : 66) == 55) if (-1 <= -1.0) { assert(true) } else { assert(false) } if (5 <= -1.0) { assert(false) } else { assert(true) } if (-1 <= 5.0) { assert(true) } else { assert(false) } let x = -1 let y = 5.0 let a1 = x <= x let b1 = y <= x let c1 = x <= y assert(a1 == true) assert(b1 == false) assert(c1 == true) if (x <= x) { assert(true) } else { assert(false) } if (y <= x) { assert(false) } else { assert(true) } if (x <= y) { assert(true) } else { assert(false) } } { // == let a = -1 == -1.0 let b = 5 == -1.0 let c = -1 == 5.0 assert(a == true) assert(b == false) assert(c == false) assert(-1 == -1.0 == true) assert(5 == -1.0 == false) assert(-1 == 5.0 == false) assert((-1 == -1.0 ? 55 : 66) == 55) assert((5 == -1.0 ? 55 : 66) == 66) assert((-1 == 5.0 ? 55 : 66) == 66) if (-1 == -1.0) { assert(true) } else { assert(false) } if (5 == -1.0) { assert(false) } else { assert(true) } if (-1 == 5.0) { assert(false) } else { assert(true) } let x = -1 let y = 5.0 let a1 = x == x let b1 = y == x let c1 = x == y assert(a1 == true) assert(b1 == false) assert(c1 == false) if (x == x) { assert(true) } else { assert(false) } if (y == x) { assert(false) } else { assert(true) } if (x == y) { assert(false) } else { assert(true) } } { // != let a = -1 != -1.0 let b = 5 != -1.0 let c = -1 != 5.0 assert(a == false) assert(b == true) assert(c == true) assert(-1 != -1.0 == false) assert(5 != -1.0 == true) assert(-1 != 5.0 == true) assert((-1 != -1.0 ? 55 : 66) == 66) assert((5 != -1.0 ? 55 : 66) == 55) assert((-1 != 5.0 ? 55 : 66) == 55) if (-1 != -1.0) { assert(false) } else { assert(true) } if (5 != -1.0) { assert(true) } else { assert(false) } if (-1 != 5.0) { assert(true) } else { assert(false) } let x = -1 let y = 5.0 let a1 = x != x let b1 = y != x let c1 = x != y assert(a1 == false) assert(b1 == true) assert(c1 == true) if (x != x) { assert(false) } else { assert(true) } if (y != x) { assert(true) } else { assert(false) } if (x != y) { assert(true) } else { assert(false) } } print("ok") ================================================ FILE: testData/exec/compare_int_float.out ================================================ ok ================================================ FILE: testData/exec/compare_int_int.nut ================================================ { // > let a = -1 > -1 let b = 5 > -1 let c = -1 > 5 assert(a == false) assert(b == true) assert(c == false) assert(-1 > -1 == false) assert(5 > -1 == true) assert(-1 > 5 == false) assert((-1 > -1 ? 55 : 66) == 66) assert((5 > -1 ? 55 : 66) == 55) assert((-1 > 5 ? 55 : 66) == 66) if (-1 > -1) { assert(false) } else { assert(true) } if (5 > -1) { assert(true) } else { assert(false) } if (-1 > 5) { assert(false) } else { assert(true) } let x = -1 let y = 5 let a1 = x > x let b1 = y > x let c1 = x > y assert(a1 == false) assert(b1 == true) assert(c1 == false) if (x > x) { assert(false) } else { assert(true) } if (y > x) { assert(true) } else { assert(false) } if (x > y) { assert(false) } else { assert(true) } } { // < let a = -1 < -1 let b = 5 < -1 let c = -1 < 5 assert(a == false) assert(b == false) assert(c == true) assert(-1 < -1 == false) assert(5 < -1 == false) assert(-1 < 5 == true) assert((-1 < -1 ? 55 : 66) == 66) assert((5 < -1 ? 55 : 66) == 66) assert((-1 < 5 ? 55 : 66) == 55) if (-1 < -1) { assert(false) } else { assert(true) } if (5 < -1) { assert(false) } else { assert(true) } if (-1 < 5) { assert(true) } else { assert(false) } let x = -1 let y = 5 let a1 = x < x let b1 = y < x let c1 = x < y assert(a1 == false) assert(b1 == false) assert(c1 == true) if (x < x) { assert(false) } else { assert(true) } if (y < x) { assert(false) } else { assert(true) } if (x < y) { assert(true) } else { assert(false) } } { // >= let a = -1 >= -1 let b = 5 >= -1 let c = -1 >= 5 assert(a == true) assert(b == true) assert(c == false) assert(-1 >= -1 == true) assert(5 >= -1 == true) assert(-1 >= 5 == false) assert((-1 >= -1 ? 55 : 66) == 55) assert((5 >= -1 ? 55 : 66) == 55) assert((-1 >= 5 ? 55 : 66) == 66) if (-1 >= -1) { assert(true) } else { assert(false) } if (5 >= -1) { assert(true) } else { assert(false) } if (-1 >= 5) { assert(false) } else { assert(true) } let x = -1 let y = 5 let a1 = x >= x let b1 = y >= x let c1 = x >= y assert(a1 == true) assert(b1 == true) assert(c1 == false) if (x >= x) { assert(true) } else { assert(false) } if (y >= x) { assert(true) } else { assert(false) } if (x >= y) { assert(false) } else { assert(true) } } { // <= let a = -1 <= -1 let b = 5 <= -1 let c = -1 <= 5 assert(a == true) assert(b == false) assert(c == true) assert(-1 <= -1 == true) assert(5 <= -1 == false) assert(-1 <= 5 == true) assert((-1 <= -1 ? 55 : 66) == 55) assert((5 <= -1 ? 55 : 66) == 66) assert((-1 <= 5 ? 55 : 66) == 55) if (-1 <= -1) { assert(true) } else { assert(false) } if (5 <= -1) { assert(false) } else { assert(true) } if (-1 <= 5) { assert(true) } else { assert(false) } let x = -1 let y = 5 let a1 = x <= x let b1 = y <= x let c1 = x <= y assert(a1 == true) assert(b1 == false) assert(c1 == true) if (x <= x) { assert(true) } else { assert(false) } if (y <= x) { assert(false) } else { assert(true) } if (x <= y) { assert(true) } else { assert(false) } } { // == let a = -1 == -1 let b = 5 == -1 let c = -1 == 5 assert(a == true) assert(b == false) assert(c == false) assert(-1 == -1 == true) assert(5 == -1 == false) assert(-1 == 5 == false) assert((-1 == -1 ? 55 : 66) == 55) assert((5 == -1 ? 55 : 66) == 66) assert((-1 == 5 ? 55 : 66) == 66) if (-1 == -1) { assert(true) } else { assert(false) } if (5 == -1) { assert(false) } else { assert(true) } if (-1 == 5) { assert(false) } else { assert(true) } let x = -1 let y = 5 let a1 = x == x let b1 = y == x let c1 = x == y assert(a1 == true) assert(b1 == false) assert(c1 == false) if (x == x) { assert(true) } else { assert(false) } if (y == x) { assert(false) } else { assert(true) } if (x == y) { assert(false) } else { assert(true) } } { // != let a = -1 != -1 let b = 5 != -1 let c = -1 != 5 assert(a == false) assert(b == true) assert(c == true) assert(-1 != -1 == false) assert(5 != -1 == true) assert(-1 != 5 == true) assert((-1 != -1 ? 55 : 66) == 66) assert((5 != -1 ? 55 : 66) == 55) assert((-1 != 5 ? 55 : 66) == 55) if (-1 != -1) { assert(false) } else { assert(true) } if (5 != -1) { assert(true) } else { assert(false) } if (-1 != 5) { assert(true) } else { assert(false) } let x = -1 let y = 5 let a1 = x != x let b1 = y != x let c1 = x != y assert(a1 == false) assert(b1 == true) assert(c1 == true) if (x != x) { assert(false) } else { assert(true) } if (y != x) { assert(true) } else { assert(false) } if (x != y) { assert(true) } else { assert(false) } } print("ok") ================================================ FILE: testData/exec/compare_int_int.out ================================================ ok ================================================ FILE: testData/exec/const_fold.nut ================================================ // Test AST-level constant folding // These expressions are fully const-evaluable and should be folded // to a single LOAD instruction during codegen. // Basic arithmetic assert(2 + 3 == 5) assert(10 - 3 == 7) assert(4 * 5 == 20) assert(15 / 3 == 5) assert(17 % 5 == 2) // Nested arithmetic assert((2 + 3) * (4 + 1) == 25) assert(1 + 2 + 3 + 4 + 5 == 15) assert(100 - 10 * 3 - 20 / 4 == 65) // String concatenation (NOT handled by peephole optimizer) assert("hello" + " " + "world" == "hello world") assert("abc" + "def" == "abcdef") // Comparison operators assert(3 > 2 == true) assert(3 < 2 == false) assert(3 >= 3 == true) assert(3 <= 2 == false) assert(3 == 3) assert(3 != 4) // Ternary with const condition assert((true ? "yes" : "no") == "yes") assert((false ? "yes" : "no") == "no") assert((1 > 0 ? 42 : 0) == 42) // Bitwise assert((0xFF & 0x0F) == 15) assert((1 << 4) == 16) assert((0xFF | 0x100) == 0x1FF) assert((0xFF ^ 0x0F) == 0xF0) assert((16 >> 2) == 4) assert((16 >>> 2) == 4) // Logical assert((true || false) == true) assert((true && false) == false) assert((false || true) == true) assert((false && true) == false) // Null coalescing assert((null ?? "default") == "default") assert(("value" ?? "default") == "value") // typeof assert(typeof 42 == "integer") assert(typeof "hello" == "string") assert(typeof 1.0 == "float") assert(typeof true == "bool") // Negation and bitwise not assert(-(-5) == 5) assert(!false == true) assert(!true == false) assert(~0 == -1) assert(~0xFF == -256) // Const identifiers in expressions const A = 10 const B = 20 assert(A + B == 30) assert(A * B + 5 == 205) assert(A > B == false) assert(A < B == true) // Enum values in expressions enum Color { RED = 1, GREEN = 2, BLUE = 4 } assert((Color.RED | Color.GREEN | Color.BLUE) == 7) assert(Color.RED + Color.GREEN == 3) // Mixed const and enum const MASK = 0xFF assert((Color.BLUE & MASK) == 4) // Complex nested const expressions assert((A + B) * 2 - A == 50) assert((A > 5 ? A : B) == 10) assert((A < 5 ? A : B) == 20) println("ok") ================================================ FILE: testData/exec/const_fold.out ================================================ ok ================================================ FILE: testData/exec/const_in_closures.nut ================================================ function foo() { const x = "asdf" function bar() { let a = x } } ================================================ FILE: testData/exec/const_in_closures.out ================================================ ================================================ FILE: testData/exec/coroutines.nut ================================================ function coroutine_test(a,b) { println($"{a} {b}") local ret = suspend("suspend 1") println($"the coroutine says {ret}") ret = suspend("suspend 2"); println($"the coroutine says {ret}") ret = suspend("suspend 3"); println($"the coroutine says {ret}") return "I'm done" } let coro = newthread(coroutine_test) local susparam = coro.call("test","coroutine") //starts the coroutine local i = 1 do { println($"suspend passed [{susparam}]") susparam = coro.wakeup("ciao "+i) ++i }while(coro.getstatus()=="suspended") println($"return passed [{susparam}]") ================================================ FILE: testData/exec/coroutines.out ================================================ test coroutine suspend passed [suspend 1] the coroutine says ciao 1 suspend passed [suspend 2] the coroutine says ciao 2 suspend passed [suspend 3] the coroutine says ciao 3 return passed [I'm done] ================================================ FILE: testData/exec/deep_loop_variable.nut ================================================ let x = { borders = [0, 0, 0, 0] } for (; x.borders[2] < 5; x.borders[2]++) println(x.borders[2]) ================================================ FILE: testData/exec/deep_loop_variable.out ================================================ 0 1 2 3 4 ================================================ FILE: testData/exec/depth_check.nut ================================================ // Tailcall // Check how deep we can go function f(n) { if (n > 0) return f(n - 1) return n } try { f(1000000) println("OK") } catch(e) { println($"Error: {e}") } ================================================ FILE: testData/exec/depth_check.out ================================================ OK ================================================ FILE: testData/exec/destructuring/foreach_capture_destruct_idx.nut ================================================ // foreach with idx + destructured val: idx is in the foreach scope (shared // across iters by default), x/y are body-block locals (per-iter via parser // desugaring). When idx is captured by an inner closure, idx must also be // rebound per-iter. let fns = [] foreach (i, {x, y} in [{x = 1, y = 2}, {x = 10, y = 20}, {x = 100, y = 200}]) { fns.append(@() i * 1000 + x * 10 + y) } foreach (f in fns) println("d1:", f()) // Same shape but idx not captured: only destructured fields captured. The // parser's body-Block scope already gives x/y per-iter; idx scan reports // no capture, so foreach uses the shared-slot path for idx. let gs = [] foreach (_i, {x, y} in [{x = 7, y = 8}, {x = 70, y = 80}]) { gs.append(@() x + y) } foreach (g in gs) println("d2:", g()) // Destructured field default that *is* a closure (it captures an outer // var, but is evaluated per-iter so closures see fresh state each time). let cap = 100 let hs = [] foreach ({n = (function() { return cap })()} in [{n = 1}, {}, {n = 3}]) { hs.append(@() n) } foreach (h in hs) println("d3:", h()) ================================================ FILE: testData/exec/destructuring/foreach_capture_destruct_idx.out ================================================ d1: 12 d1: 1120 d1: 3200 d2: 15 d2: 150 d3: 1 d3: 100 d3: 3 ================================================ FILE: testData/exec/destructuring/foreach_capture_idx_val.nut ================================================ // idx + val captured: both rebound per iteration. let fns = [] foreach (i, v in ["a", "b", "c"]) { fns.append(@() i + ":" + v) } foreach (f in fns) println("a1:", f()) // Only idx captured (val unused inside closure). let gs = [] foreach (i, _v in [100, 200, 300]) { gs.append(@() i * 2) } foreach (g in gs) println("a2:", g()) // Closure captures idx via inner named function (not just lambda). let hs = [] foreach (i, v in [7, 8, 9]) { function bind() { return i * 10 + v } hs.append(bind) } foreach (h in hs) println("a3:", h()) ================================================ FILE: testData/exec/destructuring/foreach_capture_idx_val.out ================================================ a1: 0:a a1: 1:b a1: 2:c a2: 0 a2: 2 a2: 4 a3: 7 a3: 18 a3: 29 ================================================ FILE: testData/exec/destructuring/foreach_capture_plain_val.nut ================================================ // Plain `foreach (v in arr)`: closure captures `v`. Each closure must see // its own per-iteration value (not the final shared-slot value). let fns = [] foreach (v in [10, 20, 30]) { fns.append(@() v) } foreach (f in fns) println("v1:", f()) // One-liner body form, same expectation. let gs = [] foreach (s in ["a", "b", "c"]) gs.append(@() s) foreach (g in gs) println("v2:", g()) // Nested foreach: inner closure captures both outer and inner val. let pairs = [] foreach (a in [1, 2]) { foreach (b in [10, 20]) { pairs.append(@() a * 100 + b) } } foreach (f in pairs) println("v3:", f()) ================================================ FILE: testData/exec/destructuring/foreach_capture_plain_val.out ================================================ v1: 10 v1: 20 v1: 30 v2: a v2: b v2: c v3: 110 v3: 120 v3: 210 v3: 220 ================================================ FILE: testData/exec/destructuring/foreach_capture_shadowed.nut ================================================ // Inner function declares its own param/local with the same name as an // outer foreach val. The capture scan over-approximates here (no shadow // tracking), so this triggers the per-iter path. Either way, the visible // behavior must be correct: closures returning the inner local must NOT // see the outer per-iter value, and closures actually capturing the outer // must see the per-iter value. let outerCaps = [] let innerCaps = [] foreach (v in [1, 2, 3]) { // closure captures outer v outerCaps.append(@() v) // closure shadows v with its own param (no real capture of outer) innerCaps.append(@(v) v * 10) } foreach (f in outerCaps) println("o:", f()) foreach (f in innerCaps) println("i:", f(7)) ================================================ FILE: testData/exec/destructuring/foreach_capture_shadowed.out ================================================ o: 1 o: 2 o: 3 i: 70 i: 70 i: 70 ================================================ FILE: testData/exec/destructuring/foreach_destr_default_closure.nut ================================================ let outer_factor = 10 function run() { let acc = [] foreach ({n = (function(){ return outer_factor * 5 })()} in [{n = 1}, {}, {n = 3}]) { acc.append(n) } return acc } let r = run() println(r[0], r[1], r[2]) function maker(scale) { let out = [] foreach ({v = (@(s) s + 1)(scale)} in [{}, {v = 100}, {}]) { out.append(v) } return out } let m = maker(7) println(m[0], m[1], m[2]) ================================================ FILE: testData/exec/destructuring/foreach_destr_default_closure.out ================================================ 1 50 3 8 100 8 ================================================ FILE: testData/exec/destructuring/foreach_destruct_empty_pattern.nut ================================================ // Empty `{}` and `[]` destructuring patterns: pattern matches without // binding any field. Each iteration runs body once. local n = 0 foreach ({} in [{a = 1}, {b = 2}, {}]) n++ println("e1:", n) local m = 0 foreach ([] in [[1, 2, 3], [], [9]]) m++ println("e2:", m) // Same with idx. local sumIdx = 0 foreach (i, {} in [{a = 1}, {b = 2}]) sumIdx += i println("e3:", sumIdx) // Function-arg empty destructuring is now also accepted (shared // parseDestructuringFields helper). let f = function({}) { return "ok" } println("e4:", f({a = 1})) let g = function([]) { return "ok2" } println("e5:", g([1, 2])) ================================================ FILE: testData/exec/destructuring/foreach_destruct_empty_pattern.out ================================================ e1: 3 e2: 3 e3: 1 e4: ok e5: ok2 ================================================ FILE: testData/exec/destructuring/foreach_destructuring.nut ================================================ // f1: array destructure, no idx let arr2d = [[1, 2, 3], [4, 5, 6]] foreach ([a, b, c] in arr2d) { println("f1:", a, b, c) } // f2: array destructure with idx foreach (i, [a, b, c] in arr2d) { println("f2:", i, a, b, c) } // f3: table destructure, no idx let tbls = [{x = 1, y = 2}, {x = 3, y = 4}] foreach ({x, y} in tbls) { println("f3:", x, y) } // f4: table destructure with idx foreach (i, {x, y} in tbls) { println("f4:", i, x, y) } // f5: defaults on missing keys let partial = [{x = 10}, {y = 20}, {}] foreach ({x = -1, y = -2} in partial) { println("f5:", x, y) } // f6: typed destructure with default foreach ({x: int = 0, y: int|null = null} in [{x = 5}, {y = 7}]) { println("f6:", x, y) } // f7: nested foreach destructures let nested = [ [{p = 1, q = 2}, {p = 3}], [{q = 9}] ] foreach (i, row in nested) { foreach (j, {p = 0, q = 0} in row) { println("f7:", i, j, p, q) } } // f8: mixed -- outer destructured array, inner plain foreach ([first, second] in [[10, 20], [30, 40]]) { println("f8:", first, second) } // f9: destructure value while reading idx let m = {alpha = {n = 1}, beta = {n = 2}} foreach (k, {n} in m) { println("f9:", k, n) } // f10: instance destructure class Pt { x = 0; y = 0; constructor(x_, y_) { this.x = x_; this.y = y_ } } foreach (k, {x, y} in {a = Pt(1, 2), b = Pt(3, 4)}) { println("f10:", k, x, y) } // f11: empty defaults flow when iterating same array twice let runs = [{}, {x = 5}] foreach ({x = 99} in runs) { println("f11:", x) } foreach ({x = 99} in runs) { println("f11b:", x) } // f12: capture destructured var in closure let fns = [] foreach ({v} in [{v = 1}, {v = 2}, {v = 3}]) { fns.append(@() v) } foreach (f in fns) { println("f12:", f()) } ================================================ FILE: testData/exec/destructuring/foreach_destructuring.out ================================================ f1: 1 2 3 f1: 4 5 6 f2: 0 1 2 3 f2: 1 4 5 6 f3: 1 2 f3: 3 4 f4: 0 1 2 f4: 1 3 4 f5: 10 -2 f5: -1 20 f5: -1 -2 f6: 5 null f6: 0 7 f7: 0 0 1 2 f7: 0 1 3 0 f7: 1 0 0 9 f8: 10 20 f8: 30 40 f9: beta 2 f9: alpha 1 f10: a 1 2 f10: b 3 4 f11: 99 f11: 5 f11b: 99 f11b: 5 f12: 1 f12: 2 f12: 3 ================================================ FILE: testData/exec/destructuring/foreach_destructuring_branches.nut ================================================ // B01: empty table pattern (parser: '{' immediately followed by '}') foreach ({} in [{a = 1}, {a = 2}]) println("B01") // B02: empty array pattern (parser: '[' immediately followed by ']') foreach ([] in [[1, 2], [3]]) println("B02") // B03: destructure-only val, no idx (firstIsDestr branch) foreach ({x} in [{x = 10}]) println("B03", x) // B04: idx + destructured val (idx-then-destructure branch in parser) foreach (i, {x} in [{x = 10}, {x = 20}]) println("B04", i, x) // B05: idx + plain val (existing path, regression guard) foreach (i, v in [11, 22]) println("B05", i, v) // B06: plain val (existing path, regression guard) foreach (v in [33, 44]) println("B06", v) // B07: type mask without default (runtime EmitCheckType, initializer == null) foreach ({n: int} in [{n = 1}, {n = 2}]) println("B07", n) // B08: default without type mask (initializer present, typeMask == ~0u) foreach ({m = -9} in [{m = 5}, {}]) println("B08", m) // B09: default + type mask combined (initializer present, typeMask present) foreach ({k: int = -1} in [{}, {k = 7}]) println("B09", k) // B10: nullable union default foreach ({s: string|null = null} in [{s = "ok"}, {}]) println("B10", s) // B11: array destructure index-based (DT_ARRAY codegen branch with GetNumericConstant) foreach ([a, b] in [[1, 2], [10, 20]]) println("B11", a, b) // B12: array destructure with default for missing element foreach ([a, b = 99] in [[1], [10, 20]]) println("B12", a, b) // B13: multiple decls inside one destructure with mixed shapes foreach ({a, b: int = 0, c = "?", d: int|null = null} in [ {a = 1, c = "x"}, {a = 2, b = 5, d = 42}, {a = 3} ]) println("B13", a, b, c, d) foreach ({arr} in [{arr = [1, 2]}, {arr = [3, 4, 5]}]) { local sum = 0 foreach (n in arr) sum += n println("B14", sum) } let cap = 100 foreach ({v} in [{v = 1}, {v = 2}]) { let make = function() { return v + cap } println("B15", make()) } foreach (val in [7, 8]) println("B16", val) function defv() { return 77 } foreach ({z = defv()} in [{z = 1}, {}, {z = 3}]) println("B17", z) class Acc { data = null constructor() { this.data = [] } function ingest(rows) { foreach ({tag = "?"} in rows) this.data.append(tag) } } let acc = Acc() acc.ingest([{tag = "a"}, {}, {tag = "c"}]) foreach (s in acc.data) println("B18", s) ================================================ FILE: testData/exec/destructuring/foreach_destructuring_branches.out ================================================ B01 B01 B02 B02 B03 10 B04 0 10 B04 1 20 B05 0 11 B05 1 22 B06 33 B06 44 B07 1 B07 2 B08 5 B08 -9 B09 -1 B09 7 B10 ok B10 null B11 1 2 B11 10 20 B12 1 99 B12 10 20 B13 1 0 x null B13 2 5 ? 42 B13 3 0 ? null B14 3 B14 12 B15 101 B15 102 B16 7 B16 8 B17 1 B17 77 B17 3 B18 a B18 ? B18 c ================================================ FILE: testData/exec/destructuring/foreach_destructuring_complex.nut ================================================ // ===================================================================== // 1. Three-level nested foreach with destructure at every level // ===================================================================== let world = [ {name = "north", regions = [ {tag = "a", cells = [{x = 1, y = 2}, {x = 3, y = 4}]}, {tag = "b", cells = [{x = 5, y = 6}]} ]}, {name = "south", regions = [ {tag = "c", cells = [{x = 7, y = 8}, {x = 9, y = 10}]} ]} ] foreach (i, {name, regions} in world) { foreach (j, {tag, cells} in regions) { foreach (k, {x, y} in cells) { println("L1:", i, name, j, tag, k, x, y) } } } // ===================================================================== // 2. Destructured value used inside an inner function defined per iter // ===================================================================== let factories = [] foreach (i, {label, basev} in [{label = "alpha", basev = 10}, {label = "beta", basev = 100}]) { let mult = i + 1 function build(extra) { return label + ":" + (basev * mult + extra) } factories.append(build) } foreach (f in factories) { println("L2:", f(7)) } // ===================================================================== // 3. Destructured value flows through nested functions and a class method // ===================================================================== class Counter { total = 0 function add(v) { this.total += v; return this.total } } let counter = Counter() let pipeline = [{op = "x", n = 3}, {op = "y", n = 5}, {op = "z", n = 2}] foreach (i, {op, n} in pipeline) { let local_op = op function step() { function inner() { return counter.add(n * (i + 1)) } return local_op + "->" + inner() } println("L3:", step()) } println("L3 total:", counter.total) // ===================================================================== // 4. Defaults + type masks combined with nested foreach // ===================================================================== let groups = [ {gid = 1, members = [{n = "a", age = 10}, {n = "b"}, {age = 99}]}, {gid = 2, members = []}, {gid = 3, members = [{n = "c", age = 7}]} ] foreach ({gid, members} in groups) { foreach ({n = "?", age: int|null = null} in members) { println("L4:", gid, n, age) } } // ===================================================================== // 5. Capturing destructured value per iteration via inner let-destructure // ===================================================================== let getters = [] foreach (item in [{key = "a", val = 1}, {key = "b", val = 2}, {key = "c", val = 3}]) { let {key, val} = item getters.append(@() key + "=" + val) } foreach (g in getters) { println("L5:", g()) } // ===================================================================== // 6. Modifying the source table through destructure idx alias // ===================================================================== let mutable_rows = [{count = 1}, {count = 2}, {count = 3}] foreach (i, {count} in mutable_rows) { // count is a copy of the field; mutate via index, not via destructured local mutable_rows[i].count = count * 10 } foreach ({count} in mutable_rows) { println("L6:", count) } // ===================================================================== // 7. break / continue / return inside nested destructuring loops // ===================================================================== function findFirstHigh(rows) { foreach (i, {label, scores} in rows) { foreach (j, {v} in scores) { if (v < 50) continue if (v > 200) return [label, i, j, v, "stop"] if (v > 100) return [label, i, j, v, "ok"] } } return null } let r = findFirstHigh([ {label = "first", scores = [{v = 10}, {v = 30}]}, {label = "second", scores = [{v = 70}, {v = 130}, {v = 9000}]}, {label = "third", scores = [{v = 1}]} ]) if (r != null) { let [a, b, c, d, e] = r println("L7:", a, b, c, d, e) } // ===================================================================== // 8. Destructuring over class instances with inherited fields // ===================================================================== class Base { x = 0 y = 0 constructor(x_, y_) { this.x = x_; this.y = y_ } } class Derived (Base) { z = 0 constructor(x_, y_, z_) { base.constructor(x_, y_); this.z = z_ } } let pts = [Base(1, 2), Derived(3, 4, 5), Base(6, 7), Derived(8, 9, 10)] foreach (i, {x, y, z = -1} in pts) { println("L8:", i, x, y, z) } // ===================================================================== // 9. Generator/coroutine + destructure from yielded values // ===================================================================== let gen = function() { yield {tag = "a", n = 1} yield {tag = "b", n = 2} yield {tag = "c", n = 3} } let g = gen() foreach (i, {tag, n} in g) { println("L9:", i, tag, n) } // ===================================================================== // 10. Destructured value as constructor argument and method call result // ===================================================================== class Box { v = 0 constructor(v_) { this.v = v_ } function bump(d) { this.v += d; return this } function get() { return this.v } } let configs = [{init = 100, deltas = [1, 2, 3]}, {init = 1000, deltas = [10, 20]}] let boxes = [] foreach ({init, deltas} in configs) { let b = Box(init) foreach (d in deltas) b.bump(d) boxes.append(b) } foreach (i, b in boxes) { println("L10:", i, b.get()) } // ===================================================================== // 11. Heterogeneous default types (int default vs string default vs null) // ===================================================================== let mixed = [ {n = 5, s = "hello", b = true}, {n = 7}, {s = "world"}, {}, {b = false, s = "x", n = 9} ] foreach (i, {n: int = -1, s: string = "?", b: bool|null = null} in mixed) { println("L11:", i, n, s, b) } // ===================================================================== // 12. Destructure with idx and value, then re-iterate inside body // ===================================================================== let matrix = [[1, 2, 3], [4, 5, 6], [7, 8, 9]] foreach (i, [a, b, c] in matrix) { let row_sum = a + b + c // inner foreach over the same row by index access let parts = [] foreach (cell in [a, b, c]) parts.append(cell * (i + 1)) println("L12:", i, row_sum, parts[0], parts[1], parts[2]) } // ===================================================================== // 13. Function returning destructure-friendly tables, chained // ===================================================================== function mkPair(a, b) { return {first = a, second = b} } let pairs = [mkPair(1, "one"), mkPair(2, "two"), mkPair(3, "three")] let folded = (function() { let parts = [] foreach ({first, second} in pairs) { parts.append(second + "=" + first) } return parts })() foreach (s in folded) { println("L13:", s) } // ===================================================================== // 14. Closure capture of idx + destructured field; called after loop // ===================================================================== let bound = [] foreach (i, {tag, weight} in [{tag = "p", weight = 3}, {tag = "q", weight = 7}]) { let captured_tag = tag let captured_weight = weight bound.append(function() { return captured_tag + "@" + i + "=" + captured_weight }) } foreach (f in bound) { println("L14:", f()) } // ===================================================================== // 15. Destructure in foreach inside a class method // ===================================================================== class Aggregator { data = null constructor() { this.data = [] } function ingest(rows) { foreach ({src, val = 0} in rows) { this.data.append(src + ":" + val) } } } let agg = Aggregator() agg.ingest([{src = "a", val = 1}, {src = "b"}, {src = "c", val = 9}]) foreach (s in agg.data) { println("L15:", s) } ================================================ FILE: testData/exec/destructuring/foreach_destructuring_complex.out ================================================ L1: 0 north 0 a 0 1 2 L1: 0 north 0 a 1 3 4 L1: 0 north 1 b 0 5 6 L1: 1 south 0 c 0 7 8 L1: 1 south 0 c 1 9 10 L2: alpha:17 L2: beta:207 L3: x->3 L3: y->13 L3: z->19 L3 total: 19 L4: 1 a 10 L4: 1 b null L4: 1 ? 99 L4: 3 c 7 L5: a=1 L5: b=2 L5: c=3 L6: 10 L6: 20 L6: 30 L7: second 1 1 130 ok L8: 0 1 2 -1 L8: 1 3 4 5 L8: 2 6 7 -1 L8: 3 8 9 10 L9: 0 a 1 L9: 1 b 2 L9: 2 c 3 L10: 0 106 L10: 1 1030 L11: 0 5 hello true L11: 1 7 ? null L11: 2 -1 world null L11: 3 -1 ? null L11: 4 9 x false L12: 0 6 1 2 3 L12: 1 15 8 10 12 L12: 2 24 21 24 27 L13: one=1 L13: two=2 L13: three=3 L14: p@0=3 L14: q@1=7 L15: a:1 L15: b:0 L15: c:9 ================================================ FILE: testData/exec/destructuring/foreach_no_capture.nut ================================================ // Regression guard for the zero-overhead path: when the body has no inner // function or lambda referencing idx/val, the codegen keeps idx/val in the // foreach's shared slot. Behavior must be identical to pre-capture-fix. local sum = 0 foreach (v in [1, 2, 3, 4]) sum += v println("s1:", sum) local pairs = 0 foreach (i, v in [10, 20, 30]) pairs += i * 100 + v println("s2:", pairs) // Nested function defined but doesn't reference outer idx/val: still // shared-slot path (over-approx may take the per-iter path, output is the // same either way - this asserts the semantic, not the strategy). local r = 0 foreach (v in [5, 6]) { function helper(x) { return x + 1 } r += helper(v) } println("s3:", r) // break/continue still work on shared-slot path. local first_even = -1 foreach (v in [1, 3, 5, 4, 7]) { if (v % 2 != 0) continue first_even = v break } println("s4:", first_even) ================================================ FILE: testData/exec/destructuring/foreach_no_capture.out ================================================ s1: 10 s2: 360 s3: 13 s4: 4 ================================================ FILE: testData/exec/destructuring/function_param_destructuring.nut ================================================ function t31({x}) { println("t31:", x) } t31({x = 1}) function t32({x = 10}) { println("t32:", x) } t32({}) function t33({x: int = 3, y}) { println("t33:", x, y) } t33({y = 5}) function t34({x, y: float|null}) { println("t34:", x, y) } t34({x = 1, y = null}) function t35({x = 1}, [y]) { println("t35:", x, y) } t35({}, [2]) function t36({x: int = 1}, [y: int = 2]) { println("t36:", x, y) } t36({}, []) function t37({x, y}, [a, b]) { println("t37:", x, y, a, b) } t37({x = 1, y = 2}, [3, 4]) function t38({x: int = 5}, [a, b: float = 2.2]) { println("t38:", x, a, b) } t38({}, [1]) function t39({x = null}, [a = null]) { println("t39:", x, a) } t39({}, []) function t40({x}, _) { println("t40:", x) } t40({x = 99}, []) function t41(a, {x = 10}) { println("t41:", a, x) } t41(5, {}) function t42({x = 10}, a = [1, 2, 3]) { println("t42:", a, x) } t42({}, 5) function t43({x = 10}, a, {y = 12}) { println("t43:", a, x, y) } t43({x = 2, y = 3}, 5, {}) function t44({x = 10}, a, {y: int = 12}, ...) { println("t44:", a, x, y, vargv[0]) } t44({}, 5, {e = 5}, 999) function t45([x], a, {y: int = 12}, ...) { println("t45:", a, x, y, vargv[0]) } t45([1], 5, {e = 5}, 999) let k31 = @({x}) println("k31:", x) k31({x = 1}) let k32 = @({x = 10}) println("k32:", x) k32({}) let k33 = @({x: int = 3, y}) println("k33:", x, y) k33({y = 5}) let k34 = @({x, y: float|null}) println("k34:", x, y) k34({x = 1, y = null}) let k35 = @({x = 1}, [y]) println("k35:", x, y) k35({}, [2]) let k36 = @({x: int = 1}, [y: int = 2]) println("k36:", x, y) k36({}, []) let k37 = @({x, y}, [a, b]) println("k37:", x, y, a, b) k37({x = 1, y = 2}, [3, 4]) let k38 = @({x: int = 5}, [a, b: float = 2.2]) println("k38:", x, a, b) k38({}, [1]) let k39 = @({x = null}, [a = null]) println("k39:", x, a) k39({}, []) let k40 = @({x}, _) println("k40:", x) k40({x = 99}, []) let k41 = @(a, {x = 10}) println("k41:", a, x) k41(5, {}) let k42 = @({x = 10}, a = [1, 2, 3]) println("k42:", a, x) k42({}, 5) let k43 = @({x = 10}, a, {y = 12}) println("k43:", a, x, y) k43({x = 2, y = 3}, 5, {}) let k44 = @({x = 10}, a, {y: int = 12}, ...) println("k44:", a, x, y, vargv[0]) k44({}, 5, {e = 5}, 999) let k45 = @([x], a, {y: int = 12}, ...) println("k45:", a, x, y, vargv[0]) k45([1], 5, {e = 5}, 999) //-------------------------------------------------- // N1: object destructuring inside a nested function //-------------------------------------------------- function n1() { function inner({x = 10}) { println("n1:", x) } inner({}) } n1() //-------------------------------------------------- // N2: array destructuring inside a nested function //-------------------------------------------------- function n2() { function inner([a, b = 2]) { println("n2:", a, b) } inner([1]) } n2() //-------------------------------------------------- // N3: closure + destructuring //-------------------------------------------------- function n3() { let offset = 5 function inner({x = 1}) { println("n3:", x + offset) } inner({}) } n3() //-------------------------------------------------- // N4: outer argument + inner destructuring //-------------------------------------------------- function n4({x = 3}) { function inner([y = 4]) { println("n4:", x, y) } inner([]) } n4({}) //-------------------------------------------------- // N5: name shadowing in a nested function //-------------------------------------------------- function n5({x = 1}) { function inner({x = 10}) { println("n5:", x) } inner({}) } n5({x = 2}) //-------------------------------------------------- // N6: types in a nested function //-------------------------------------------------- function n6() { function inner({x: int = 7}, [y: int = 8]) { println("n6:", x, y) } inner({}, []) } n6() //-------------------------------------------------- // N7: multiple levels of nested functions //-------------------------------------------------- function n7() { function mid({x = 1}) { function inner([y = 2]) { println("n7:", x, y) } inner([]) } mid({}) } n7() //-------------------------------------------------- // N8: passing destructured values further //-------------------------------------------------- function n8() { function mid({x = 3}) { function inner(v) { println("n8:", v) } inner(x) } mid({}) } n8() //-------------------------------------------------- // N9: destructuring + null in a nested function //-------------------------------------------------- function n9() { function inner({x: int|null = null}) { println("n9:", x) } inner({}) } n9() //-------------------------------------------------- // N10: destructuring + varargs in a nested function //-------------------------------------------------- function n10() { function inner({x = 1}, ...) { println("n10:", x, vargv[0]) } inner({}, 99) } n10() function c1( cfg = { handlers = [ function({x = 1}) { function inner({y = 2}) { println("c1:", x, y) } inner({}) } ] } ) { cfg.handlers[0]({}) } c1() function c2( data = { make = function() { return function( opts = { run = function([a = 3, b = 4]) { function inner({x = 5}) { println("c2:", a, b, x) } inner({}) } } ) { opts.run([]) } } } ) { let f = data.make() f() } c2() function e1( cfg = class { counter = 0 function next({step = 1}) { this.counter += step return this.counter } }() ) { println("e1:", cfg.next({}), cfg.next({step = 2})) } e1() e1() function e2( box = { fns = [ function({x = 1}) { return function([y = 2]) { println("e2:", x, y) } } ] } ) { let g = box.fns[0]({}) g([]) } e2() function e3( state = { init = function() { return function({x = 10}, ...) { println("e3:", x, vargv.len()) } } } ) { let f = state.init() f({}, 1, 2, 3) } e3() function e4( make = function( cfg = { wrap = function(fn) { return function({x = 1}) { fn(x) } } } ) { return cfg.wrap( function(v) { println("e4:", v) } ) } ) { let f = make() f({}) } e4() function e5( cfg = { a = function({x = 1}) { function b([y = 2]) { function c({z = 3}) { println("e5:", x, y, z) } c({}) } b([]) } } ) { cfg.a({}) } e5() function t2({a = {x = 0}}) { a.x++ println("t2:", a.x) } t2({}) t2({}) function t3([arr = []]) { arr.append(1) println("t3:", arr.len()) } t3([]) t3([]) function t4( [cfg: table = { count = 0, inc = function() { this.count++ return this.count } }] ) { println("t4:", cfg.inc()) } t4([]) t4([]) function t5( cfg = { make = function() { return function({x = {y = 0}}) { x.y++ println("t5:", x.y) } } } ) { let f = cfg.make() f({}) } t5() t5() ================================================ FILE: testData/exec/destructuring/function_param_destructuring.out ================================================ t31: 1 t32: 10 t33: 3 5 t34: 1 null t35: 1 2 t36: 1 2 t37: 1 2 3 4 t38: 5 1 2.2 t39: null null t40: 99 t41: 5 10 t42: 5 10 t43: 5 2 12 t44: 5 10 12 999 t45: 5 1 12 999 k31: 1 k32: 10 k33: 3 5 k34: 1 null k35: 1 2 k36: 1 2 k37: 1 2 3 4 k38: 5 1 2.2 k39: null null k40: 99 k41: 5 10 k42: 5 10 k43: 5 2 12 k44: 5 10 12 999 k45: 5 1 12 999 n1: 10 n2: 1 2 n3: 6 n4: 3 4 n5: 10 n6: 7 8 n7: 1 2 n8: 3 n9: null n10: 1 99 c1: 1 2 c2: 3 4 5 e1: 1 3 e1: 4 6 e2: 1 2 e3: 10 3 e4: 1 e5: 1 2 3 t2: 1 t2: 1 t3: 1 t3: 1 t4: 1 t4: 1 t5: 1 t5: 1 ================================================ FILE: testData/exec/div64_by_minus_one_opt.nut ================================================ let min_int = -0x7FFFFFFF_FFFFFFFF-1 try { print( min_int / -1 ) } catch(e) { print("OK") } ================================================ FILE: testData/exec/div64_by_minus_one_opt.out ================================================ OK ================================================ FILE: testData/exec/div64_by_minus_one_vm.nut ================================================ try { local x = -0x7FFFFFFFFFFFFFFF - 1 local y = @(a) -a print( x / y(1) ) } catch (e) { print("OK") } ================================================ FILE: testData/exec/div64_by_minus_one_vm.out ================================================ OK ================================================ FILE: testData/exec/div_by_minus_one_opt.nut ================================================ print( (-0x7FFFFFFF-1) / -1 ) ================================================ FILE: testData/exec/div_by_minus_one_opt.out ================================================ 2147483648 ================================================ FILE: testData/exec/div_by_minus_one_vm.nut ================================================ local x = -0x7FFFFFFF - 1 local y = @(a) -a print( x / y(1) ) ================================================ FILE: testData/exec/div_by_minus_one_vm.out ================================================ 2147483648 ================================================ FILE: testData/exec/fallback_get_recursion.nut ================================================ // Demonstrates SQVM::FallBackGet <-> SQVM::Get recursion (1.3) // // Note: The pure delegation chain path (table -> delegate -> table) // requires setdelegate() which is a C API only, not exposed to script. // But the SAME FallBackGet function also handles OT_INSTANCE via _get // metamethod (sqvm.cpp:2172-2188), which re-enters Get(). // // Call chain for `a.missing_key` where a is instance of class with _get: // GetImpl(a, "missing_key") // sqvm.cpp:2053 // -> key not found in instance slots // -> FallBackGet(a, "missing_key") // sqvm.cpp:2159 // -> case OT_INSTANCE: // sqvm.cpp:2172 // -> GetMetaMethod(MT_GET) -> found _get // -> Call(_get, [a, "missing_key"]) // sqvm.cpp:2178 // -> Execute() runs _get body // -> body accesses b.missing_key // -> GetImpl(b, "missing_key") // -> FallBackGet(b, "missing_key") // -> Call(_get, [b, "missing_key"]) // -> body accesses a.missing_key <-- infinite loop! local depth = 0 local a = null local b = null class A { other = null function _get(key) { depth++ if (depth <= 5) println($" A._get('{key}') depth={depth}, forwarding to B...") // Accessing .missing on the other instance triggers // Get -> FallBackGet -> _get on that instance return this.other.missing } } class B { other = null function _get(key) { depth++ if (depth <= 5) println($" B._get('{key}') depth={depth}, forwarding to A...") return this.other.missing } } a = A() b = B() a.other = b b.other = a println("Accessing a.x ...") try { // This triggers: Get(a,"x") -> FallBackGet -> _get -> Get(b,"missing") // -> FallBackGet -> _get -> Get(a,"missing") -> ... infinite local val = a.x println($"val = {val}") } catch(e) { println($" ... (skipping depths 6..{depth})") println($"Caught after depth={depth}: {e}") } ================================================ FILE: testData/exec/fallback_get_recursion.out ================================================ Accessing a.x ... A._get('x') depth=1, forwarding to B... B._get('missing') depth=2, forwarding to A... A._get('missing') depth=3, forwarding to B... B._get('missing') depth=4, forwarding to A... A._get('missing') depth=5, forwarding to B... ... (skipping depths 6..99) Caught after depth=99: Error in '_get' metamethod: Native stack overflow ================================================ FILE: testData/exec/fuzzer_seed_106.nut ================================================ // #disable-optimizer // seed = 106 try{ local count = 0 let to_log = function(id, s) { let tp = typeof(s); print($"{id}: "); print(tp == "integer" || tp == "float" || tp == "string" ? s : tp); print("\n"); if (count++ > 1000) print["stop"]; } { let t6 = { } let a7 = [ (-5), ] let t54 = { f0=-3317007163456823286 f1=-4 } let t55 = { } let a57 = [ ] let v58 = -2 let v60 = 3 let t59 = { f0=((v60) & (11)) } let a61 = [ 4, 11, ] let t62 = { } let a64 = [ (((13 > 10 - 9)) ? ((6)) : (4)), ] let v65 = (((7 != 8)) ? ((((-5 == -4)) ? (5) : (6))) : (2)) - 9 let a63 = [ 5, (a64?[0] ?? v65), ] let t56 = { f0=-1 f1=(((((((a57?[0] ?? 7) == v58)) ? (11) : (2)) == ((-3 - (((((4) | (-4)) < ((1) & (3)))) ? (-4 - 10) : (13 - 467615902768935684))) & (10)))) ? ((t59?["f1"] ?? ((5) & (10))) + (((((((10 >= 2)) ? (1) : ((a61?[1] ?? (t62?["f1"] ?? 8))))) > 10 + 0 + (((10 == 6)) ? (9) : (2)))) ? (6) : ((-1)))) : ((a63?[1] ?? -2))) } let a66 = [ ] let t67 = { f0=2 } let fni68 = @() 12 let v69 = 13 let fni70 = @() 8 let a71 = [ ] let v72 = v60 let v74 = -2 let a73 = [ (v74), ] let v76 = -3 let v75 = v76 let t78 = { } let a79 = [ ] let t80 = { } let t81 = { } let v82 = 8 { local loop0 = 1 while (loop0++ < -4) { let v2 = (-3) let v3 = 9 let a4 = [ ] let v5 = (2) let t1 = { f0=v2 f1=(v3 + (a4?[1] ?? -5 - v5)) } continue to_log("1", t1.f1) to_log("2", v2) to_log("3", v3) to_log("4", a4) to_log("5", v5) to_log("6", t1) } } { local loop8 = (t6?.f0 ?? a7[0]) while (loop8++ < -1) { let gen11 = function () { yield 14; yield 7; yield 6; } let t52 = { } let a53 = [ 12 + -5, -3, ] foreach (t9, t10 in gen11()) { let v51 = (((t52?["f1"] ?? (13))) & ((((a53?[1] ?? -1)) & (-5)))) break to_log("7", 1) break if (-2) { let v50 = v51 let fn12 = function fn12(p28, p30, p34) { let fn13 = function fn13() { let v16 = {m0 = -5, m1 = 6, m2 = 11, } let fn17 = function fn17(p18) { to_log("8", (((4 > 12)) ? (-4) : (11))) to_log("9", 8) to_log("10", 5 + p18) try { let v19 = 7 to_log("11", v19) to_log("12", -3) to_log("13", v19) } catch(err20) { to_log("14", ((11) | (9))) } } foreach (t14, t15 in v16) fn17(7) to_log("15", v16) to_log("16", fn17) } let v22 = 6 let fni21 = @() ((v22) | (((9) & (10)))) let a25 = [ 1, ] let v27 = (((-3 <= -5)) ? (2) : (v22)) let fni26 = @() v27 let v29 = -1 fn13() { local loop23 = ((-4500801829576294150) | (fni21())) while (loop23++ < (a25?[0] ?? fni26())) { let a24 = [ ] to_log("17", 7) to_log("18", (a24?[1] ?? (-1))) to_log("19", a24) } } to_log("20", t10) to_log("21", -1 - p28) if (v29) to_log("22", 7) else { let fni49 = @() 12 to_log("23", p30) try { to_log("24", 14) 0/0 } catch(err43) { let gen33 = function () { return 2; yield 13; yield 14; } let fn35 = function fn35() { let v37 = (((3 == -4)) ? (-1) : (3)) let gen42 = function () { } for (local loop36 = 0; loop36 < v37; loop36++) { let fn38 = function fn38() { let v39 = 5 to_log("29", v39) to_log("30", 5) to_log("31", v39) } to_log("28", -8447515169283609172) fn38() to_log("32", fn38) } to_log("33", 5) foreach (t40, t41 in gen42()) { if (!((9 <= 2))) { to_log("34", -2) } to_log("35", -1) if (!((-1 > 8))) { to_log("36", -1) to_log("37", (((14 == 13)) ? (-3) : (12))) to_log("38", 2) to_log("39", 0) to_log("40", 3) } to_log("41", 13) to_log("42", -3) } to_log("43", 4) to_log("44", v37) to_log("45", gen42) } foreach (t31, t32 in gen33()) { to_log("25", p34) to_log("26", 6) to_log("27", ((11) | (-4))) } fn35() to_log("46", gen33) to_log("47", fn35) } for (local loop44 = 5; loop44 < 5; loop44++) { let t45 = { f0=-2 f1=14 } let fni46 = @() 3 let a47 = [ -4, ] let v48 = 13 to_log("48", 11) to_log("49", 5) to_log("50", (((t45.f1)) & (fni46())) - (((2 != 5793629195146322613)) ? (14) : (8))) to_log("51", (((a47[0] == 9)) ? (((787389375239146782 - 12) | (v48))) : (10))) to_log("52", t45) to_log("53", fni46) to_log("54", a47) to_log("55", v48) } to_log("56", fni49()) to_log("57", fni49) } to_log("58", fn13) to_log("59", v22) to_log("60", fni21) to_log("61", a25) to_log("62", v27) to_log("63", fni26) to_log("64", v29) } fn12(3, ((v50) & (v50)), (13)) to_log("65", 1) to_log("66", v50) to_log("67", fn12) } to_log("68", v51) } to_log("69", gen11) to_log("70", t52) to_log("71", a53) } } { local loop77 = (((11 == (t54?.f0 ?? 12))) ? ((t55?["f0"] ?? 7)) : (((-1) & (((t56?["f1"] ?? ((10) & (-3))) + (a66?[0] ?? (t67?["f0"] ?? (((v65 < -4)) ? (-2) : (((13) | (6)))))) + fni68()))))) + (((v65 != v69)) ? ((((fni70() > (((-1 >= -2)) ? (0) : ((a71?[0] ?? v65))))) ? (-5) : (v72 - (a73?[1] ?? v75)))) : (-3)) do to_log("72", 11) while (loop77++ < (t78?["f1"] ?? ((5 - (((8 <= (a79?[0] ?? 14))) ? (-5) : (12)) + (((8 <= ((((t80?.f0 ?? -4) <= (t81?["f0"] ?? -5))) ? (2) : (14)))) ? (10) : (6))) & (6)))) } to_log("73", -1) { local loop83 = (((11 >= (((v82) + 10) & (-5)))) ? (-6760683509134506842) : (2)) while (loop83++ < 11) { to_log("74", 0) } } to_log("75", t6) to_log("76", a7) to_log("77", t54) to_log("78", t55) to_log("79", a57) to_log("80", v58) to_log("81", v60) to_log("82", t59) to_log("83", a61) to_log("84", t62) to_log("85", a64) to_log("86", v65) to_log("87", a63) to_log("88", t56) to_log("89", a66) to_log("90", t67) to_log("91", fni68) to_log("92", v69) to_log("93", fni70) to_log("94", a71) to_log("95", v72) to_log("96", v74) to_log("97", a73) to_log("98", v76) to_log("99", v75) to_log("100", t78) to_log("101", a79) to_log("102", t80) to_log("103", t81) to_log("104", v82) }} catch (q) {print(q)} ================================================ FILE: testData/exec/fuzzer_seed_106.out ================================================ 69: function 70: table 71: array 69: function 70: table 71: array 69: function 70: table 71: array 69: function 70: table 71: array 72: 11 73: -1 74: 0 74: 0 74: 0 74: 0 74: 0 74: 0 74: 0 74: 0 74: 0 75: table 76: array 77: table 78: table 79: array 80: -2 81: 3 82: table 83: array 84: table 85: array 86: -3 87: array 88: table 89: array 90: table 91: function 92: 13 93: function 94: array 95: 3 96: -2 97: array 98: -3 99: -3 100: table 101: array 102: table 103: table 104: 8 ================================================ FILE: testData/exec/fuzzer_seed_3250.nut ================================================ //#disable-optimizer let v2 = 10 let v34 = -10 let v33 = (v2 > v34) ? v34 : 8 println(v33) println("ok") ================================================ FILE: testData/exec/fuzzer_seed_3250.out ================================================ -10 ok ================================================ FILE: testData/exec/fuzzer_seed_7200.nut ================================================ let v0 = -11 if (v0 > v0) { } print(v0) ================================================ FILE: testData/exec/fuzzer_seed_7200.out ================================================ -11 ================================================ FILE: testData/exec/import_correct.nut ================================================ from "math" import sin, cos, abs from "string" import format, printf from "io" import * import "datetime" import "debug" as Debug // Test that keywords work as identifiers local import = 123 local from = [1, 2, 3] function useKeywords() { println("import value:", import) println("from length:", from.len()) } useKeywords() println(sin(0), abs(-5)) ================================================ FILE: testData/exec/import_correct.out ================================================ import value: 123 from length: 3 0 5 ================================================ FILE: testData/exec/inexpr_block/inexpr_block_1.nut ================================================ #allow-compiler-internals let a = $${return 123;} println(a) ================================================ FILE: testData/exec/inexpr_block/inexpr_block_1.out ================================================ 123 ================================================ FILE: testData/exec/inexpr_block/inexpr_block_2.nut ================================================ #allow-compiler-internals println($${return 123;}) ================================================ FILE: testData/exec/inexpr_block/inexpr_block_2.out ================================================ 123 ================================================ FILE: testData/exec/inexpr_block/inexpr_block_3.nut ================================================ #allow-compiler-internals let x = 555 let a = $${return 123;} let y = 888 println(x, a, y) ================================================ FILE: testData/exec/inexpr_block/inexpr_block_3.out ================================================ 555 123 888 ================================================ FILE: testData/exec/inexpr_block/inexpr_block_4.nut ================================================ #allow-compiler-internals let a = 654 + $${return 123;} println(a) ================================================ FILE: testData/exec/inexpr_block/inexpr_block_4.out ================================================ 777 ================================================ FILE: testData/exec/inexpr_block/inexpr_block_5.nut ================================================ #allow-compiler-internals let a = $${return 123;} + 654 println(a) ================================================ FILE: testData/exec/inexpr_block/inexpr_block_5.out ================================================ 777 ================================================ FILE: testData/exec/inexpr_block/inexpr_block_6.nut ================================================ #allow-compiler-internals println(654 + $${return 123;}) ================================================ FILE: testData/exec/inexpr_block/inexpr_block_6.out ================================================ 777 ================================================ FILE: testData/exec/inexpr_block/inexpr_block_7.nut ================================================ #allow-compiler-internals println($${return 123;} + 654) ================================================ FILE: testData/exec/inexpr_block/inexpr_block_7.out ================================================ 777 ================================================ FILE: testData/exec/inexpr_block/inexpr_block_8.nut ================================================ #allow-compiler-internals let lam = @(a, b) $${ return a - b } let xx = $${ let x = 4 function fn(a) { return $${return a != 4 ? a * 2 : 3} } println(lam(fn(x), 100)) } println(xx) ================================================ FILE: testData/exec/inexpr_block/inexpr_block_8.out ================================================ -97 null ================================================ FILE: testData/exec/inexpr_block/test_codeblock.nut ================================================ #allow-compiler-internals let x = 0 try { let a = $${ local t = 5 + x if (t > 4) t /= x return t } println(a) } catch (e) { println(e) } ================================================ FILE: testData/exec/inexpr_block/test_codeblock.out ================================================ division by zero ================================================ FILE: testData/exec/inexpr_block/test_codeblock_return_in_try.nut ================================================ #allow-compiler-internals try { let a = $${ return 42 } println($"a = {a}") assert(a == 42) } catch (e) { println($"caught: {e}") } println("DONE") ================================================ FILE: testData/exec/inexpr_block/test_codeblock_return_in_try.out ================================================ a = 42 DONE ================================================ FILE: testData/exec/inexpr_block/test_codeblock_try.nut ================================================ #allow-compiler-internals // Test 1: simple return from CodeBlockExpr inside try try { let a = $${ return 42 } assert(a == 42, $"expected 42, got {a}") } catch (e) { assert(false, $"unexpected exception: {e}") } // Test 2: conditional return from CodeBlockExpr inside try try { let b = $${ local x = 10 if (x > 5) return x * 2 return -1 } assert(b == 20, $"expected 20, got {b}") } catch (e) { assert(false, $"unexpected exception: {e}") } // Test 3: exception INSIDE CodeBlockExpr is caught by outer try local caught = false try { let c = $${ throw "boom" return 0 } assert(false, "should not reach here") } catch (e) { caught = true assert(e == "boom", $"expected 'boom', got '{e}'") } assert(caught, "exception should have been caught") // Test 4: try inside CodeBlockExpr let d = $${ try { throw "inner" return -1 } catch (e) { return 99 } } assert(d == 99, $"expected 99, got {d}") // Test 5: try inside CodeBlockExpr inside try try { let e = $${ try { throw "nested" return -1 } catch (ex) { return 77 } } assert(e == 77, $"expected 77, got {e}") } catch (ex) { assert(false, $"unexpected exception: {ex}") } // Test 6: foreach inside CodeBlockExpr inside try try { let f = $${ local sum = 0 foreach (v in [1, 2, 3, 4, 5]) sum += v return sum } assert(f == 15, $"expected 15, got {f}") } catch (e) { assert(false, $"unexpected exception: {e}") } // Test 7: null return from CodeBlockExpr inside try try { let g = $${ return null } assert(g == null, $"expected null, got {g}") } catch (e) { assert(false, $"unexpected exception: {e}") } // Test 8: bare return (no value) from CodeBlockExpr inside try try { let h = $${ return } assert(h == null, $"expected null, got {h}") } catch (e) { assert(false, $"unexpected exception: {e}") } print("ALL CODEBLOCK+TRY TESTS PASSED\n") ================================================ FILE: testData/exec/inexpr_block/test_codeblock_try.out ================================================ ALL CODEBLOCK+TRY TESTS PASSED ================================================ FILE: testData/exec/integers.nut ================================================ assert(0x0 == 0) assert(0x1 == 1) assert(0xf == 15) assert(0x0F == 15) assert(0x00F == 15) assert(0x000F == 15) assert(0x0000F == 15) assert(0x00000F == 15) assert(0x000000F == 15) assert(0x0000000F == 15) assert(0x00000000F == 15) assert(0x000000000F == 15) assert(0x0000000000F == 15) assert(0x00000000000F == 15) assert(0x000000000000F == 15) assert(0x0000000000000F == 15) assert(0x00000000000000F == 15) assert(0x000000000000000F == 15) assert(0x00000_0000000_000F == 15) assert(0x_____________________________________________________________________________________________________________________F == 15) assert(0xf0123456789aBcDE == -1147797409030816546) assert(99999999999999999 + 1234567890 == 100000001234567889) assert('A' == 65) assert('\t' == 9) assert(' ' == 32) assert("\U12"[0] == 0x12) assert("\U7F"[0] == 0x7F) assert(~0 == -1) assert(~1 == -2) assert(0xFFFFFFFFFFFFFFFE == -2) assert(0x8000000000000000 == -9223372036854775807 - 1) assert(100 + 10 + 1 == 111) assert(111 - 10 - 1 == 100) assert(100 * 10 * 1 == 1000) assert(100 / 10 / 1 == 10) assert(101 % 10 % 1 == 0) local a100 = 100 local a10 = 10 let a1 = a10 / 10 local a111 = a100 + a1 + a10 local a101 = a100 + a1 assert(a100 + a10 + a1 == 111) assert(a111 - a10 - a1 == a100) assert(a100 * a10 * a1 == 1000) assert(a100 / a10 / a1 == a10) assert(a101 % a10 % a1 == 0) assert((100 << 10 << 1) == 100 * 1024 * 2) assert((100 >> 1) == 50) assert((0x8000000000000000 >>> 63) == 1) assert(0x0000000000000000.tostring() == "0") assert(0x8000000000000000.tostring() == "-9223372036854775808") assert(0x7FFFFFFFFFFFFFFF.tostring() == "9223372036854775807") assert(0x8000000000000000 == "-9223372036854775808".tointeger()) assert(0x7FFFFFFFFFFFFFFF == "9223372036854775807".tointeger()) assert((true).tointeger() == 1) assert((false).tointeger() == 0) assert(!!0x8000000000000000 == true) assert(!!0x0000000000000000 == false) assert(0x8000000000000000 + 0x8000000000000001 == 1) assert([11 12 13][2] == 13) assert([11,12,-13][2] == -13) assert([11,12 13][2] == 13) assert([11,12 13,][2] == 13) assert([11 12 - 13][1] == -1) local a = 1 assert((a *= -10) == -10) assert((a *= -10) == 100) assert((a *= -10) == -1000) assert((a *= -10) == 10000) assert((a *= -10) == -100000) assert((a *= -10) == 1000000) assert((a *= -10) == -10000000) assert((a *= -10) == 100000000) assert((a *= -10) == -1000000000) assert((a *= -10) == 10000000000) assert((a *= -10) == -100000000000) ================================================ FILE: testData/exec/integers.out ================================================ ================================================ FILE: testData/exec/is_frozen.nut ================================================ let a1 = [10, 20, 30] let a2 = freeze([10, 20, 30]) println($"Array R: {a1.is_frozen()}") println($"Array F: {a2.is_frozen()}") let t1 = { a = 10, b = 20, c = 30 } let t2 = freeze({ a = 10, b = 20, c = 30 }) println($"Table R: {t1.is_frozen()}") println($"Table F: {t2.is_frozen()}") let C = class {} let F = freeze(class {}) println($"Class R: {C.is_frozen()}") println($"Class F: {F.is_frozen()}") let ri = C() let fi = freeze(C()) println($"Instance R: {ri.is_frozen()}") println($"Instance F: {fi.is_frozen()}") let ai = freeze([{}, {}]) foreach(i, v in ai) println($"Array foreach {i}: {v.is_frozen()}") ai.each(@(v, i) println($"Array each {i}: {v.is_frozen()}")) let ti = freeze({[{a=1}]={}, [{a=2}]={}}) foreach(k, v in ti) println($"Table foreach: {k.is_frozen()} {v.is_frozen()}") ti.each(@(v, k) println($"Table each: {k.is_frozen()} {v.is_frozen()}")) ================================================ FILE: testData/exec/is_frozen.out ================================================ Array R: false Array F: true Table R: false Table F: true Class R: false Class F: true Instance R: false Instance F: true Array foreach 0: true Array foreach 1: true Array each 0: true Array each 1: true Table foreach: true true Table foreach: true true Table each: true true Table each: true true ================================================ FILE: testData/exec/locals_chain.nut ================================================ local a=5, b=a, c=a, d=b println($"{a}, {b}, {c}, {d}") let x=6, y=x, z=x, w=y println($"{x}, {y}, {z}, {w}") ================================================ FILE: testData/exec/locals_chain.out ================================================ 5, 5, 5, 5 6, 6, 6, 6 ================================================ FILE: testData/exec/metamethod_error.nut ================================================ class SetThrows { function _set(key, val) { throw "Not supported" } } class GetThrows { function _get(key) { throw "Read denied" } } class ThrowsNonString { function _set(key, val) { throw 42 } } // Test _set string error try { let x = SetThrows() x.foo = 123 assert(false, "should have thrown") } catch (e) { assert(e == "Error in '_set' metamethod: Not supported", $"unexpected: {e}") } // Test _get string error try { let x = GetThrows() let _ = x.foo assert(false, "should have thrown") } catch (e) { assert(e == "Error in '_get' metamethod: Read denied", $"unexpected: {e}") } // Test non-string error object try { let x = ThrowsNonString() x.foo = 123 assert(false, "should have thrown") } catch (e) { assert(e == "Error in '_set' metamethod: '42' (type='integer')", $"unexpected: {e}") } println("ALL PASSED") ================================================ FILE: testData/exec/metamethod_error.out ================================================ ALL PASSED ================================================ FILE: testData/exec/mod64_by_minus_one_opt.nut ================================================ print( (-0x7FFFFFFF_FFFFFFFF-1) % -1 ) ================================================ FILE: testData/exec/mod64_by_minus_one_opt.out ================================================ 0 ================================================ FILE: testData/exec/mod64_by_minus_one_vm.nut ================================================ local x = -0x7FFFFFFF_FFFFFFFF - 1 local y = @(a) -a print( x % y(1) ) ================================================ FILE: testData/exec/mod64_by_minus_one_vm.out ================================================ 0 ================================================ FILE: testData/exec/mod_by_minus_one_opt.nut ================================================ print( (-0x7FFFFFFF-1) % -1 ) ================================================ FILE: testData/exec/mod_by_minus_one_opt.out ================================================ 0 ================================================ FILE: testData/exec/mod_by_minus_one_vm.nut ================================================ local x = -0x7FFFFFFF - 1 local y = @(a) -a print( x % y(1) ) ================================================ FILE: testData/exec/mod_by_minus_one_vm.out ================================================ 0 ================================================ FILE: testData/exec/native_fields.nut ================================================ // Test native field access (MEMBER_TYPE_NATIVE_FIELD) // NativeVec has float x,y,z and int32 w as native fields from "test.native" import NativeVec // Basic read after construction local v = NativeVec(1.0, 2.0, 3.0, 42) println($"construct: {v.x} {v.y} {v.z} {v.w}") // Write float fields v.x = 10.0 v.y = 20.0 v.z = 30.0 println($"write float: {v.x} {v.y} {v.z}") // Write int field v.w = 100 println($"write int: {v.w}") // Type coercion: assign int to float field v.x = 7 println($"int to float: {v.x}") // Type coercion: assign float to int field v.w = 3.14 println($"float to int: {v.w}") // Default construction (zeros) local v2 = NativeVec() println($"default: {v2.x} {v2.y} {v2.z} {v2.w}") // Multiple instances are independent v.x = 100.0 println($"independent: {v.x} {v2.x}") // _tostring works alongside native fields println(v2.tostring()) // Increment on int native field v.w = 5 v.w++ println($"w after ++: {v.w}") v.w-- println($"w after --: {v.w}") // Increment on float native field v.x = 1.5 v.x++ println($"x after ++: {v.x}") v.x-- println($"x after --: {v.x}") // Compound assignment v.w = 10 v.w += 5 println($"w after +=5: {v.w}") v.x = 2.0 v.x *= 3.0 println($"x after *=3: {v.x}") // Clone copies native field values v.x = 11.0 v.y = 22.0 v.z = 33.0 v.w = 44 local vc = clone v println($"clone: {vc.x} {vc.y} {vc.z} {vc.w}") vc.x = 0.0 println($"clone independent: {v.x} {vc.x}") // foreach over instance reads native fields local v3 = NativeVec(1.0, 2.0, 3.0, 99) local keys = [] local vals = [] foreach (k, val in v3) { keys.append(k) vals.append(val) } // Native fields should appear in enumeration println($"foreach has x: {keys.indexof("x") != null}") println($"foreach has w: {keys.indexof("w") != null}") println($"foreach x val: {vals[keys.indexof("x")]}") println($"foreach w val: {vals[keys.indexof("w")]}") // 'in' operator println($"x in v: {"x" in v}") println($"w in v: {"w" in v}") println($"q in v: {"q" in v}") // Non-literal key access (goes through _OP_GET/_OP_SET, not _OP_GET_LITERAL/_OP_SET_LITERAL) local key = "x" v[key] = 77.0 println($"non-literal get: {v[key]}") // Type mismatch errors // Literal path (_OP_SET_LITERAL) try { v.x = "hello" } catch(e) println($"err literal: {e}") // Non-literal path (_OP_SET) try { local k = "x"; v[k] = "hello" } catch(e) println($"err non-literal: {e}") // Null assignment try { v.x = null } catch(e) println($"err null: {e}") println("PASSED") ================================================ FILE: testData/exec/native_fields.out ================================================ construct: 1 2 3 42 write float: 10 20 30 write int: 100 int to float: 7 float to int: 3 default: 0 0 0 0 independent: 100 0 NativeVec(0, 0, 0, 0) w after ++: 6 w after --: 5 x after ++: 2.5 x after --: 1.5 w after +=5: 15 x after *=3: 6 clone: 11 22 33 44 clone independent: 11 0 foreach has x: true foreach has w: true foreach x val: 1 foreach w val: 99 x in v: true w in v: true q in v: false non-literal get: 77 err literal: type mismatch in native field assignment err non-literal: type mismatch in native field assignment err null: type mismatch in native field assignment PASSED ================================================ FILE: testData/exec/opt/fuzz_52807_min.nut ================================================ local v1 = -111 v1 = 12 local loop2 = v1 - v1 println(v1) println(loop2) ================================================ FILE: testData/exec/opt/fuzz_52807_min.out ================================================ 12 0 ================================================ FILE: testData/exec/opt/fuzz_min.nut ================================================ local v1 = -111 v1 = 3 local loop2 = v1 - -5 + 12 println(v1) println(loop2) ================================================ FILE: testData/exec/opt/fuzz_min.out ================================================ 3 20 ================================================ FILE: testData/exec/opt/modify_local_var.nut ================================================ //#disable-optimizer local cnt1 = 11 cnt1 = 222 cnt1 -= cnt1 / 3333 println(cnt1) ================================================ FILE: testData/exec/opt/modify_local_var.out ================================================ 222 ================================================ FILE: testData/exec/opt/opt_reassign_addi.nut ================================================ // Test: reassigned local used in ADDI fold (2-instruction fold: LOAD, ADDI) // The peephole folds LOADINT+MOVE into LOADINT targeting the local's register. // The ADDI fold must not replace that LOADINT with a result targeting a different register. // Pattern: local x = A; x = B; local y = x + C + D // After peephole: LOADINT x_reg B, ADDI y_reg C x_reg, ADDI y_reg D y_reg // The first ADDI fold must not destroy x's value. { local v1 = -111 v1 = 3 local loop2 = v1 - -5 + 12 assert(v1 == 3, $"expected v1==3, got {v1}") assert(loop2 == 20, $"expected loop2==20, got {loop2}") } { local a = 100 a = 5 local b = a + 10 assert(a == 5, $"expected a==5, got {a}") assert(b == 15, $"expected b==15, got {b}") } { local a = 100 a = 5 local b = a + 10 + 20 assert(a == 5, $"expected a==5, got {a}") assert(b == 35, $"expected b==35, got {b}") } // Chain of ADDI folds { local x = -1 x = 7 local y = x + 1 + 2 + 3 assert(x == 7, $"expected x==7, got {x}") assert(y == 13, $"expected y==13, got {y}") } // Negative immediates { local x = 999 x = 20 local y = x - 5 - 3 assert(x == 20, $"expected x==20, got {x}") assert(y == 12, $"expected y==12, got {y}") } println("opt_reassign_addi: OK") ================================================ FILE: testData/exec/opt/opt_reassign_addi.out ================================================ opt_reassign_addi: OK ================================================ FILE: testData/exec/opt/opt_reassign_arith.nut ================================================ // Test: reassigned local used in arithmetic (3-instruction fold: LOAD, LOAD, ARITH) // The peephole optimizer merges LOADINT+MOVE into a dual-purpose LOADINT. // The post-pass const folder must not destroy the reassignment side effect. // Pattern: local x = A; x = B; x -= x / C // After peephole: LOADINT reg B (also assigns x), LOADINT tmp C, DIV, SUB // The fold must preserve x = B. { local x = 11 x = 222 x -= x / 3333 assert(x == 222, $"expected 222, got {x}") } // Same with addition { local x = 99 x = 10 x += x / 5 assert(x == 12, $"expected 12, got {x}") } // Same with multiplication { local x = 99 x = 7 x *= x + 1 // Can't fold x*x at compile time, but ensure x==7 assert(x == 56, $"expected 56, got {x}") } // Reassign then binary op where result goes to different register { local x = 50 x = 10 local y = x + 5 assert(x == 10, $"expected x==10, got {x}") assert(y == 15, $"expected y==15, got {y}") } // Reassign then binary op with two constants after peephole { local x = 50 x = 10 local y = x * 3 + 1 assert(x == 10, $"expected x==10, got {x}") assert(y == 31, $"expected y==31, got {y}") } println("opt_reassign_arith: OK") ================================================ FILE: testData/exec/opt/opt_reassign_arith.out ================================================ opt_reassign_arith: OK ================================================ FILE: testData/exec/opt/opt_reassign_chain.nut ================================================ // Test: chained constant expressions after reassignment. // Ensures multi-step folding doesn't lose intermediate assignments. // Two-step chain: x = B; y = x op const1 op const2 { local x = 100 x = 5 local y = x + 10 + 20 assert(x == 5, $"expected x==5, got {x}") assert(y == 35, $"expected y==35, got {y}") } // Subtraction chain { local x = 100 x = 50 local y = 100 - x - 10 assert(x == 50, $"expected x==50, got {x}") assert(y == 40, $"expected y==40, got {y}") } // Mixed operations { local a = 0 a = 6 local b = 0 b = 7 local c = a + b assert(a == 6, $"expected a==6, got {a}") assert(b == 7, $"expected b==7, got {b}") assert(c == 13, $"expected c==13, got {c}") } // Multiple reassignments before use { local x = 1 x = 2 x = 3 local y = x + 10 assert(x == 3, $"expected x==3, got {x}") assert(y == 13, $"expected y==13, got {y}") } // Reassign used in comparison (JCMP fold path) { local x = 100 x = 5 local r = x < 10 assert(x == 5, $"expected x==5, got {x}") assert(r, $"expected x < 10 to be true") } // Reassign with float { local x = 1.0 x = 3.0 local y = x + 1.0 assert(x == 3.0, $"expected x==3.0, got {x}") assert(y == 4.0, $"expected y==4.0, got {y}") } // Reassign float, same-register fold { local x = 1.0 x = 5.0 local y = x - x assert(x == 5.0, $"expected x==5.0, got {x}") assert(y == 0.0, $"expected y==0.0, got {y}") } // JCMP same-register: both sides of comparison are the same reassigned variable (seed 916086 pattern) { local v0 = -111 v0 = -3 if (v0 >= v0) assert(true, "v0 >= v0 should be true") else assert(false, "v0 >= v0 was false - optimizer used stale register value") } // JCMP same-register with different initial value { local v0 = 999 v0 = 42 if (v0 == v0) assert(true, "v0 == v0 should be true") else assert(false, "v0 == v0 was false") } // JCMP same-register: less-than (always false for same value) { local v0 = 100 v0 = 7 if (v0 < v0) assert(false, "v0 < v0 should be false") else assert(true, "v0 < v0 is correctly false") } println("opt_reassign_chain: OK") ================================================ FILE: testData/exec/opt/opt_reassign_chain.out ================================================ opt_reassign_chain: OK ================================================ FILE: testData/exec/opt/opt_reassign_in_scope.nut ================================================ // Test: reassigned locals used across nested scopes and control flow. // Ensures optimizer respects liveness in more complex code patterns. // Reassign inside block, use after { local x = 10 x = 20 local y = x + 1 if (y > 0) { assert(x == 20, $"expected x==20, got {x}") } assert(y == 21, $"expected y==21, got {y}") } // Reassign, use in loop init { local x = 100 x = 3 local sum = 0 for (local i = x + 1; i < 10; i++) sum += i assert(x == 3, $"expected x==3, got {x}") assert(sum == 4+5+6+7+8+9, $"expected sum==39, got {sum}") } // Reassign, use in function call argument { local x = 50 x = 7 local arr = [x + 1, x + 2, x + 3] assert(x == 7, $"expected x==7, got {x}") assert(arr[0] == 8, $"expected arr[0]==8, got {arr[0]}") assert(arr[1] == 9, $"expected arr[1]==9, got {arr[1]}") assert(arr[2] == 10, $"expected arr[2]==10, got {arr[2]}") } // Multiple locals reassigned { local a = 1 local b = 2 a = 10 b = 20 local c = a + b assert(a == 10, $"expected a==10, got {a}") assert(b == 20, $"expected b==20, got {b}") assert(c == 30, $"expected c==30, got {c}") } // Reassign with compound expression using self { local x = 100 x = 8 x = x + 2 // x should be 10 local y = x + 5 assert(x == 10, $"expected x==10, got {x}") assert(y == 15, $"expected y==15, got {y}") } println("opt_reassign_in_scope: OK") ================================================ FILE: testData/exec/opt/opt_reassign_in_scope.out ================================================ opt_reassign_in_scope: OK ================================================ FILE: testData/exec/opt/opt_same_reg_fold.nut ================================================ // Test: both loads in 3-instruction fold target the same register. // When loadA and loadB write to the same register, loadA's value is dead // (overwritten by loadB). The optimizer must not use loadA's stale value. // Pattern: local x = A; x = B; local y = x - x // After peephole: LOADINT reg A, LOADINT reg B, SUB y_reg reg reg // Both loads target the same register. Result must be B - B = 0, not A - B. { local v1 = -111 v1 = 12 local loop2 = v1 - v1 assert(v1 == 12, $"expected v1==12, got {v1}") assert(loop2 == 0, $"expected loop2==0, got {loop2}") } { local x = 99 x = 5 local y = x + x assert(x == 5, $"expected x==5, got {x}") assert(y == 10, $"expected y==10, got {y}") } { local x = 99 x = 7 local y = x * x assert(x == 7, $"expected x==7, got {x}") assert(y == 49, $"expected y==49, got {y}") } // Division by self { local x = 50 x = 13 local y = x / x assert(x == 13, $"expected x==13, got {x}") assert(y == 1, $"expected y==1, got {y}") } // Modulo by self { local x = 50 x = 13 local y = x % x assert(x == 13, $"expected x==13, got {x}") assert(y == 0, $"expected y==0, got {y}") } // Bitwise XOR with self { local x = 50 x = 0xFF local y = x ^ x assert(x == 0xFF, $"expected x==255, got {x}") assert(y == 0, $"expected y==0, got {y}") } // Bitwise AND with self { local x = 50 x = 0xAB local y = x & x assert(x == 0xAB, $"expected x==171, got {x}") assert(y == 0xAB, $"expected y==171, got {y}") } println("opt_same_reg_fold: OK") ================================================ FILE: testData/exec/opt/opt_same_reg_fold.out ================================================ opt_same_reg_fold: OK ================================================ FILE: testData/exec/opt/sqf916086.nut ================================================ //#disable-optimizer // seed = 916086 try{ local count = 0 let to_log = function(id, s) { let tp = typeof(s); print($"{id}: "); print(tp == "integer" || tp == "float" || tp == "string" ? s : tp); print("\n"); if (count++ > 1000) print["stop"]; } { local v0 = -111 v0 = -3 if (v0 >= v0) to_log("1", 8) }} catch (q) {print(q)} print("===\n") ================================================ FILE: testData/exec/opt/sqf916086.out ================================================ 1: 8 === ================================================ FILE: testData/exec/optimizer.nut ================================================ println("OPTIMIZER OFF") function test_opt_off() { #disable-optimizer for (local i = 0; i < (true ? 5 : 1); i++) println("X") } test_opt_off() println("OPTIMIZER ON") function test_opt_on() { #enable-optimizer for (local i = 0; i < (true ? 5 : 1); i++) println("X") } test_opt_on() ================================================ FILE: testData/exec/optimizer.out ================================================ OPTIMIZER OFF X X X X X OPTIMIZER ON X X X X X ================================================ FILE: testData/exec/optimizer_add.nut ================================================ let x = 0x7FFF_FFFF let y = 0x1 assert(x + y == 0x8000_0000) assert(0x7FFF_FFFF + 0x1 == 0x8000_0000) ================================================ FILE: testData/exec/optimizer_add.out ================================================ ================================================ FILE: testData/exec/optimizer_mul.nut ================================================ let x = 0x100_0000 let y = 0x100 assert(x * y == 0x1_0000_0000) assert(0x100_0000 * 0x100 == 0x1_0000_0000) ================================================ FILE: testData/exec/optimizer_mul.out ================================================ ================================================ FILE: testData/exec/parenCallee.nut ================================================ let f = @(v) v + 1 function foo() { return ((((f))))(10) } ================================================ FILE: testData/exec/parenCallee.out ================================================ ================================================ FILE: testData/exec/ph_optimizer_null_call_1.nut ================================================ let x = [@() 444] let z = 0 println(x[z]?()) ================================================ FILE: testData/exec/ph_optimizer_null_call_1.out ================================================ 444 ================================================ FILE: testData/exec/ph_optimizer_null_call_2.nut ================================================ let tab = { x = [ @() 444 ] } local zero = 0 println(tab?.x[zero]()) ================================================ FILE: testData/exec/ph_optimizer_null_call_2.out ================================================ 444 ================================================ FILE: testData/exec/ph_optimizer_null_call_3.nut ================================================ let x = [[[[@() 444]]]] let z = 0 println(x?[z][z][z][z]()) ================================================ FILE: testData/exec/ph_optimizer_null_call_3.out ================================================ 444 ================================================ FILE: testData/exec/runtime_type_check/assign_type_01.nut ================================================ let x = [4.0][0] let y: float = x return y ================================================ FILE: testData/exec/runtime_type_check/assign_type_01.out ================================================ ================================================ FILE: testData/exec/runtime_type_check/assign_type_02.nut ================================================ local y: table|null = null return y ================================================ FILE: testData/exec/runtime_type_check/assign_type_02.out ================================================ ================================================ FILE: testData/exec/runtime_type_check/assign_type_03.nut ================================================ local y: array = [] y.append(333) y = [] return y ================================================ FILE: testData/exec/runtime_type_check/assign_type_03.out ================================================ ================================================ FILE: testData/exec/runtime_type_check/assign_type_04.nut ================================================ local y: int = 5 y = 8 y += 4 println(y) ================================================ FILE: testData/exec/runtime_type_check/assign_type_04.out ================================================ 12 ================================================ FILE: testData/exec/runtime_type_check/assign_type_05.nut ================================================ local y: array = [] function fn() { y.append(333) y = [] } fn() return y ================================================ FILE: testData/exec/runtime_type_check/assign_type_05.out ================================================ ================================================ FILE: testData/exec/runtime_type_check/assign_type_06.nut ================================================ local y: int = 5 function fn() { y = 8 y += 4 } fn() println(y) ================================================ FILE: testData/exec/runtime_type_check/assign_type_06.out ================================================ 12 ================================================ FILE: testData/exec/runtime_type_check/assign_type_07.nut ================================================ let arr = [{}] let [y: table] = arr return y ================================================ FILE: testData/exec/runtime_type_check/assign_type_07.out ================================================ ================================================ FILE: testData/exec/runtime_type_check/assign_type_08.nut ================================================ let tab = { x = "ttt" } let { x: string|null } = tab return x ================================================ FILE: testData/exec/runtime_type_check/assign_type_08.out ================================================ ================================================ FILE: testData/exec/runtime_type_check/assign_type_09.nut ================================================ let arr = [{x = 5}] let [y: table = {}] = arr return y ================================================ FILE: testData/exec/runtime_type_check/assign_type_09.out ================================================ ================================================ FILE: testData/exec/runtime_type_check/assign_type_10.nut ================================================ local x = {} function fn(x, v: string|table = x) { return v } fn(0, "123") ================================================ FILE: testData/exec/runtime_type_check/assign_type_10.out ================================================ ================================================ FILE: testData/exec/runtime_type_check/assign_wrong_type_01.nut ================================================ let x = [4.0][0] let y: table = x return y ================================================ FILE: testData/exec/runtime_type_check/assign_wrong_type_01.out ================================================ AN ERROR HAS OCCURRED [type 'float' differs from the declared type 'table'] CALLSTACK *FUNCTION [__main__()] testData/exec/runtime_type_check/assign_wrong_type_01.nut:2 LOCALS [x] 4 [vargv] ARRAY=[] [this] NULL Error [Failed to run script testData/exec/runtime_type_check/assign_wrong_type_01.nut (testData/exec/runtime_type_check/assign_wrong_type_01.nut)] ================================================ FILE: testData/exec/runtime_type_check/assign_wrong_type_02.nut ================================================ local y: table|null = 4 return y ================================================ FILE: testData/exec/runtime_type_check/assign_wrong_type_02.out ================================================ ERROR: expression of type 'int' cannot be assigned to type 'table|null' testData/exec/runtime_type_check/assign_wrong_type_02.nut:1:0 local y: table|null = 4 ^---------------------- return y Error [Failed to compile file testData/exec/runtime_type_check/assign_wrong_type_02.nut (testData/exec/runtime_type_check/assign_wrong_type_02.nut)] ================================================ FILE: testData/exec/runtime_type_check/assign_wrong_type_03.nut ================================================ local y: array = [] y.append(333) y = {} return y ================================================ FILE: testData/exec/runtime_type_check/assign_wrong_type_03.out ================================================ ERROR: expression of type 'table' cannot be assigned to type 'array' testData/exec/runtime_type_check/assign_wrong_type_03.nut:3:0 y.append(333) y = {} ^ return y Error [Failed to compile file testData/exec/runtime_type_check/assign_wrong_type_03.nut (testData/exec/runtime_type_check/assign_wrong_type_03.nut)] ================================================ FILE: testData/exec/runtime_type_check/assign_wrong_type_04.nut ================================================ local y: int = 5 y = 8 y += 4.5 println(y) ================================================ FILE: testData/exec/runtime_type_check/assign_wrong_type_04.out ================================================ AN ERROR HAS OCCURRED [type 'float' differs from the declared type 'int'] CALLSTACK *FUNCTION [__main__()] testData/exec/runtime_type_check/assign_wrong_type_04.nut:3 LOCALS [y] 12.5 [vargv] ARRAY=[] [this] NULL Error [Failed to run script testData/exec/runtime_type_check/assign_wrong_type_04.nut (testData/exec/runtime_type_check/assign_wrong_type_04.nut)] ================================================ FILE: testData/exec/runtime_type_check/assign_wrong_type_05.nut ================================================ local y: array = [] function fn() { y.append(333) y = {} } fn() return y ================================================ FILE: testData/exec/runtime_type_check/assign_wrong_type_05.out ================================================ ERROR: expression of type 'table' cannot be assigned to type 'array' testData/exec/runtime_type_check/assign_wrong_type_05.nut:5:4 y.append(333) y = {} ^ } Error [Failed to compile file testData/exec/runtime_type_check/assign_wrong_type_05.nut (testData/exec/runtime_type_check/assign_wrong_type_05.nut)] ================================================ FILE: testData/exec/runtime_type_check/assign_wrong_type_06.nut ================================================ local y: int = 5 function fn() { y = 8 y += 4.5 } fn() println(y) ================================================ FILE: testData/exec/runtime_type_check/assign_wrong_type_06.out ================================================ AN ERROR HAS OCCURRED [type 'float' differs from the declared type 'int'] CALLSTACK *FUNCTION [fn()] testData/exec/runtime_type_check/assign_wrong_type_06.nut:5 *FUNCTION [__main__()] testData/exec/runtime_type_check/assign_wrong_type_06.nut:10 LOCALS [y] 8 [this] NULL [fn] CLOSURE=FN:fn [y] 8 [vargv] ARRAY=[] [this] NULL Error [Failed to run script testData/exec/runtime_type_check/assign_wrong_type_06.nut (testData/exec/runtime_type_check/assign_wrong_type_06.nut)] ================================================ FILE: testData/exec/runtime_type_check/assign_wrong_type_07.nut ================================================ let arr = [4.0] let [y: table] = arr return y ================================================ FILE: testData/exec/runtime_type_check/assign_wrong_type_07.out ================================================ AN ERROR HAS OCCURRED [type 'float' differs from the declared type 'table'] CALLSTACK *FUNCTION [__main__()] testData/exec/runtime_type_check/assign_wrong_type_07.nut:2 LOCALS [y] 4 [arr] ARRAY=[4] [vargv] ARRAY=[] [this] NULL Error [Failed to run script testData/exec/runtime_type_check/assign_wrong_type_07.nut (testData/exec/runtime_type_check/assign_wrong_type_07.nut)] ================================================ FILE: testData/exec/runtime_type_check/assign_wrong_type_08.nut ================================================ let tab = { x = 4.0 } let { x: string|null } = tab return x ================================================ FILE: testData/exec/runtime_type_check/assign_wrong_type_08.out ================================================ AN ERROR HAS OCCURRED [type 'float' differs from the declared type 'string|null'] CALLSTACK *FUNCTION [__main__()] testData/exec/runtime_type_check/assign_wrong_type_08.nut:2 LOCALS [x] 4 [tab] TABLE={x=4} [vargv] ARRAY=[] [this] NULL Error [Failed to run script testData/exec/runtime_type_check/assign_wrong_type_08.nut (testData/exec/runtime_type_check/assign_wrong_type_08.nut)] ================================================ FILE: testData/exec/runtime_type_check/assign_wrong_type_09.nut ================================================ let arr = [4.0] let [y: table = "test"] = arr return y ================================================ FILE: testData/exec/runtime_type_check/assign_wrong_type_09.out ================================================ ERROR: expression of type 'string' cannot be assigned to type 'table' testData/exec/runtime_type_check/assign_wrong_type_09.nut:2:5 let arr = [4.0] let [y: table = "test"] = arr ^---------------- return y Error [Failed to compile file testData/exec/runtime_type_check/assign_wrong_type_09.nut (testData/exec/runtime_type_check/assign_wrong_type_09.nut)] ================================================ FILE: testData/exec/runtime_type_check/assign_wrong_type_10.nut ================================================ local x = null function fn(x, v: string|table = x) { return v } fn(0, "123") ================================================ FILE: testData/exec/runtime_type_check/assign_wrong_type_10.out ================================================ AN ERROR HAS OCCURRED [default param (2) type 'null' differs from the declared type 'string|table'] CALLSTACK *FUNCTION [__main__()] testData/exec/runtime_type_check/assign_wrong_type_10.nut:3 LOCALS [x] NULL [vargv] ARRAY=[] [this] NULL Error [Failed to run script testData/exec/runtime_type_check/assign_wrong_type_10.nut (testData/exec/runtime_type_check/assign_wrong_type_10.nut)] ================================================ FILE: testData/exec/runtime_type_check/return_type.nut ================================================ function fn(x: int, y: any): int { return x * y } println(fn(5, 2)) ================================================ FILE: testData/exec/runtime_type_check/return_type.out ================================================ 10 ================================================ FILE: testData/exec/runtime_type_check/return_wrong_type.nut ================================================ function fn(x: int, y: any): int { return x * y } println(fn(5, 1.25)) ================================================ FILE: testData/exec/runtime_type_check/return_wrong_type.out ================================================ AN ERROR HAS OCCURRED [type 'float' differs from the declared type 'int'] CALLSTACK *FUNCTION [fn()] testData/exec/runtime_type_check/return_wrong_type.nut:3 *FUNCTION [__main__()] testData/exec/runtime_type_check/return_wrong_type.nut:6 LOCALS [y] 1.25 [x] 5 [this] NULL [fn] CLOSURE=FN:fn [vargv] ARRAY=[] [this] NULL Error [Failed to run script testData/exec/runtime_type_check/return_wrong_type.nut (testData/exec/runtime_type_check/return_wrong_type.nut)] ================================================ FILE: testData/exec/spec/class.nut ================================================ class Foo { //constructor testy = null constructor(a) { this.testy = ["stuff",1,2,3,a] } //member function function PrintTesty() { foreach(i,val in this.testy) { println("idx = "+i+" = "+val) } } //property } let foo = Foo(42) foo.PrintTesty() ================================================ FILE: testData/exec/spec/class.out ================================================ idx = 0 = stuff idx = 1 = 1 idx = 2 = 2 idx = 3 = 3 idx = 4 = 42 ================================================ FILE: testData/exec/spec/classInher.nut ================================================ class Foo { function DoSomething() { println("I'm the base") } function DoIt() { this.DoSomething() } } class SuperFoo(Foo) { //overridden method function DoSomething() { println("I'm the derived") } function DoIt() { base.DoIt() } } //creates a new instance of SuperFoo let inst = SuperFoo() //prints "I'm the derived" inst.DoIt() ================================================ FILE: testData/exec/spec/classInher.out ================================================ I'm the derived ================================================ FILE: testData/exec/spec/class_extend.nut ================================================ class B {} class A(B) {} class D(class {}) {} class E(class(class {}) {}) {} class F(class(class(class{}) {}) {}) {} class G(class { a = 1 }) { a = 2 } { println(1) } { println(2) } ================================================ FILE: testData/exec/spec/class_extend.out ================================================ 1 2 ================================================ FILE: testData/exec/spec/clone.nut ================================================ let t = { a = 10, b = 20 } let ct0 = clone t #forbid-clone-operator let ct1 = t.clone() #allow-clone-operator let ct2 = clone t #forbid-clone-operator let ct3 = t.clone() println($"Table t->a: original = {t.a}, cloned op = {ct2.a}, cloned method {ct3.a}") println($"Table t->b: original = {t.b}, cloned op = {ct2.b}, cloned method {ct3.b}") let arr = ["a", "b", "c"] let arr1 = arr.clone() #allow-clone-operator let arr2 = clone arr println($"Array[0]: original = {arr[0]}, cloned op = {arr2[0]}, cloned method {arr1[0]}") println($"Array[1]: original = {arr[1]}, cloned op = {arr2[1]}, cloned method {arr1[1]}") println($"Array[2]: original = {arr[2]}, cloned op = {arr2[2]}, cloned method {arr1[2]}") let s = "test" let s1 = clone s #forbid-clone-operator let s2 = s.clone() println($"String: orig '{s}', cloned op '{s1}', cloned method '{s2}'") class C { x = 10 } let obj = C() let obj1 = obj.clone() #allow-clone-operator let obj2 = clone obj println($"Instance: orig {obj.x}, cloned op {obj2.x}, cloned method {obj1.x}") try { let c1 = clone C println("Class: clone op -- FAILED") } catch (e) { println($"Class: clone op -- OK, exception '{e}'") } #forbid-clone-operator try { let c1 = C.clone() println("Class: clone method -- FAILED") } catch (e) { println($"Class: clone method -- OK, exception '{e}'") } let inum = 10 let fnum = 5.5 let inum1 = inum.clone() let fnum1 = fnum.clone() #allow-clone-operator let inum2 = clone inum let fnum2 = clone fnum println($"Integer: orig {inum}, cloned op {inum2}, cloned method {inum1}") println($"Float: orig {fnum}, cloned op {fnum2}, cloned method {fnum1}") let cls = @(x) $"x = '{x}'" let cls1 = clone cls #forbid-clone-operator let cls2 = cls.clone() println($"Closure: orig {cls(42)}, cloned op {cls1(42)}, cloned method {cls2(42)}") ================================================ FILE: testData/exec/spec/clone.out ================================================ Table t->a: original = 10, cloned op = 10, cloned method 10 Table t->b: original = 20, cloned op = 20, cloned method 20 Array[0]: original = a, cloned op = a, cloned method a Array[1]: original = b, cloned op = b, cloned method b Array[2]: original = c, cloned op = c, cloned method c String: orig 'test', cloned op 'test', cloned method 'test' Instance: orig 10, cloned op 10, cloned method 10 Class: clone op -- OK, exception 'cloning a class' Class: clone method -- OK, exception 'cloning a class' Integer: orig 10, cloned op 10, cloned method 10 Float: orig 5.5, cloned op 5.5, cloned method 5.5 Closure: orig x = '42', cloned op x = '42', cloned method x = '42' ================================================ FILE: testData/exec/spec/closure.nut ================================================ function foo(a, b, c) { if (a) { local x = 10 local z = 20 if (b) { local w = 30 local y = 40 if (c) { local u = 490 return function(q) { return q + u + w } } } else { return function(t) { return t + x } } } } ================================================ FILE: testData/exec/spec/closure.out ================================================ ================================================ FILE: testData/exec/spec/conditionalFor.nut ================================================ for (local i = 0; i < (true ? 5 : 1); i++) print("X") ================================================ FILE: testData/exec/spec/conditionalFor.out ================================================ XXXXX ================================================ FILE: testData/exec/spec/const.nut ================================================ const a = 5 const b = "foo" const c = null const d = [1, 2, 3, {x=555}] const e = b const f = d[1] const g = d[3].x println(d[3].x) println(e) println(f) println(g) // Inline constant let x = const [5,6,7,d,e] println(x[2]) let y = 2>3 ? const [1,2,3] : const [8,9,0] println(y[2]) ================================================ FILE: testData/exec/spec/const.out ================================================ 555 foo 2 555 7 0 ================================================ FILE: testData/exec/spec/constFolding.nut ================================================ let a = 1 let c = a + 1 print(c) let b = 1 let d = 2 + b print(d) let e = 3 let f = 4 let g = e + f print(g) let v0 = 1 if (v0 > v0) { } print(v0) ================================================ FILE: testData/exec/spec/constFolding.out ================================================ 2371 ================================================ FILE: testData/exec/spec/constFoldingCond.nut ================================================ print((((((-14 != 24)) ? (242) : (264)) + -12 - 78) & (157))) ================================================ FILE: testData/exec/spec/constFoldingCond.out ================================================ 152 ================================================ FILE: testData/exec/spec/const_func.nut ================================================ const function non_pure() { println("Non-pure, can be declared as constant, but can't be called as constant initializer") } non_pure() const double = @[pure](x) x*2 const quad = const @[pure](x) double(double(x)) const r0 = quad(5) println(r0) const square = function [pure](x) { return x*x } const r1 = square(5) println(r1) const greet = const @[pure](name = "world") $"Hello, {name}" println(greet()) println(greet("Kitty")) const function [pure] cube(x) { return x*x*x } println(cube(3)) const function [pure] add(a, b) { return a + b } println(add(10, 20)) global const function [pure] multiply(x, y) { return x * y } println(multiply(4, 5)) const function[pure] container(x) { function nested(y) { return x*y } return nested(5) } const z = container(111) println(z) const table = { function foo() {} bar = @() 12345 } println(table.bar()) ================================================ FILE: testData/exec/spec/const_func.out ================================================ Non-pure, can be declared as constant, but can't be called as constant initializer 20 25 Hello, world Hello, Kitty 27 30 20 555 12345 ================================================ FILE: testData/exec/spec/const_func_freevars.nut ================================================ const with_outer_vars_1 = @" let w = 123 const function[pure] container2(x) { function nested(y) { return x*y*w //<----- } return nested(5) } const z2 = container2(111) println(z) " try { compilestring(with_outer_vars_1) } catch (e) { println(e) } const with_outer_vars_2 = @" let w = 123 const function[pure] container3(x) { function nested(y) { return x*y } return nested(5)*w //<----- } const z2 = container2(111) println(z) " try { compilestring(with_outer_vars_2) } catch (e) { println(e) } ================================================ FILE: testData/exec/spec/const_func_freevars.out ================================================ ERROR: const function cannot have outer variables ERROR: const function cannot have outer variables ================================================ FILE: testData/exec/spec/const_math_eval.nut ================================================ const tests = [ "const a = 1 / 0" "const a = 1 % 0" "const a = (-0x7FFFFFFF-1) / -1" "const a = (-0x7FFFFFFF_FFFFFFFF-1) / -1" "const a = (-0x7FFFFFFF_FFFFFFFF-1) % -1" "const a = (-0x7FFFFFFF_FFFFFFFF-1) % (-0x7FFFFFFF_FFFFFFFF-1)" "const a = 0x7FFFFFFF_FFFFFFFF + 1" "const a = (-0x7FFFFFFF_FFFFFFFF-1) - 1" "const a = 0x7FFFFFFF_FFFFFFFF * 2" "const a = (-0x7FFFFFFF_FFFFFFFF-1) * -1" "const a = -(-0x7FFFFFFF-1)" "const a = -(-0x7FFFFFFF_FFFFFFFF-1)" "const a = 1 / 0.0" "const a = -1 / 0.0" "const a = 1 % 0.0" "const a = 0.0 / 0.0" "const a = 1 / -0.0" "const a = 1e309" "const a = 1e-324" "const a = (0.0/0.0) + 1.0" // "const a = 1 << -1" // "const a = 1 << 64" "const a = 0x80000000 << 1" "const a = (-1) >> 1" "const a = (-1) >>> 1" ] foreach (test in tests) { println($"Testing [{test}]") let src = $"{test}\nprintln(a)" try { let closure = compilestring(src, "test", {println}) closure() } catch (e) { println(e) } println() } ================================================ FILE: testData/exec/spec/const_math_eval.out ================================================ Testing [const a = 1 / 0] ERROR: integer division by zero Testing [const a = 1 % 0] ERROR: integer modulo by zero Testing [const a = (-0x7FFFFFFF-1) / -1] 2147483648 Testing [const a = (-0x7FFFFFFF_FFFFFFFF-1) / -1] ERROR: integer overflow Testing [const a = (-0x7FFFFFFF_FFFFFFFF-1) % -1] 0 Testing [const a = (-0x7FFFFFFF_FFFFFFFF-1) % (-0x7FFFFFFF_FFFFFFFF-1)] 0 Testing [const a = 0x7FFFFFFF_FFFFFFFF + 1] -9223372036854775808 Testing [const a = (-0x7FFFFFFF_FFFFFFFF-1) - 1] 9223372036854775807 Testing [const a = 0x7FFFFFFF_FFFFFFFF * 2] -2 Testing [const a = (-0x7FFFFFFF_FFFFFFFF-1) * -1] -9223372036854775808 Testing [const a = -(-0x7FFFFFFF-1)] 2147483648 Testing [const a = -(-0x7FFFFFFF_FFFFFFFF-1)] -9223372036854775808 Testing [const a = 1 / 0.0] ERROR: float division by zero Testing [const a = -1 / 0.0] ERROR: float division by zero Testing [const a = 1 % 0.0] ERROR: float modulo by zero Testing [const a = 0.0 / 0.0] ERROR: float division by zero Testing [const a = 1 / -0.0] ERROR: float division by zero Testing [const a = 1e309] ERROR: float constant overflow Testing [const a = 1e-324] ERROR: float constant underflow Testing [const a = (0.0/0.0) + 1.0] ERROR: float division by zero Testing [const a = 0x80000000 << 1] 4294967296 Testing [const a = (-1) >> 1] -1 Testing [const a = (-1) >>> 1] 9223372036854775807 ================================================ FILE: testData/exec/spec/const_with_expr.nut ================================================ // Unary const o = 4 const a = -o const b = !false const c = ~a const d = typeof(c) println(a, b, c, d) // Binary and ternary const tbl = {["integer"] = 5} const x = tbl[typeof o] + (o | 0xFF) + (o % 3) - (b ? 6 : o) println(x) ================================================ FILE: testData/exec/spec/const_with_expr.out ================================================ -4 true 3 integer 255 ================================================ FILE: testData/exec/spec/delegate_get.nut ================================================ let t = { rawget = function (k) { return $"HAHA -- {k}" }, a = "10", b = "Foo Foo" } let x = t.$rawget("b") let y = t.rawget("b") println($"rawget(\"b\") = {y}, $rawget(\"b\") = {x}") let z = t?.$foo let nz = t?.foo println($"t?.$foo = {z}, t?.foo = {nz}") try { let vf = t.$bar println("FAIL: '$bar' is not a built-in member of table but successfully accessed") } catch (e) { println($"OK: '$bar' is not a built-in member of table and access raised exectpion: \"{e}\"") } #forbid-implicit-type-methods println("Forbid implicit type methods") try { let rg1 = t.rawget println("OK: 'rawget' is field in table") } catch (e) { println($"FAIL: 'rawget' is field in table. Exception: \"{e}\"") } try { let ks = t.keys println("FAIL: 'keys' is built-in method of table which are forbiden.") } catch (e) { println($"OK: 'keys' is built-in method of table. Exception: \"{e}\"") } try { let rg2 = t.$rawget println("OK: 'rawget' is built-in method of table accessed via '$'") } catch (e) { println($"FAIL: 'rawget' is built-in method of table accessed via '$'. Exception: \"{e}\"") } try { let nks = t?.keys let nks2 = t?.$keys println($"OK: Safe access of built-in method 'keys' in both '$' and regular cases succeseded") } catch (e) { println($"FAIL: Safe access of built-in method 'keys' failed. Exception: \"{e}\"") } #allow-implicit-type-methods println("Allow implicit type methods") try { let rg1 = t.rawget println("OK: 'rawget' is field in table") } catch (e) { println($"FAIL: 'rawget' is field in table. Exception: \"{e}\"") } try { let ks = t.keys println($"OK: 'keys' is built-in method of table.") } catch (e) { println("FAIL: 'keys' is built-in method of table which are forbiden.") } try { let rg2 = t.$rawget println("OK: 'rawget' is built-in method of table accessed via '$'") } catch (e) { println($"FAIL: 'rawget' is built-in method of table accessed via '$'. Exception: \"{e}\"") } ================================================ FILE: testData/exec/spec/delegate_get.out ================================================ rawget("b") = HAHA -- b, $rawget("b") = Foo Foo t?.$foo = null, t?.foo = null OK: '$bar' is not a built-in member of table and access raised exectpion: "the index 'bar' (type='string') does not exist" Forbid implicit type methods OK: 'rawget' is field in table OK: 'keys' is built-in method of table. Exception: "the index 'keys' (type='string') does not exist" OK: 'rawget' is built-in method of table accessed via '$' OK: Safe access of built-in method 'keys' in both '$' and regular cases succeseded Allow implicit type methods OK: 'rawget' is field in table OK: 'keys' is built-in method of table. OK: 'rawget' is built-in method of table accessed via '$' ================================================ FILE: testData/exec/spec/destruct.nut ================================================ let arr = [123, 567] let [a, b = 22, c = 11] = arr println(a) // => 123 println(b) // => 567 println(c) // => 567 function foo() { return {x = 555, y=777, z=999, w=111} } let {x, y=1, q=3} = foo() println(x) // => 555 println(y) // => 777 println(q) // => 3 ================================================ FILE: testData/exec/spec/destruct.out ================================================ 123 567 11 555 777 3 ================================================ FILE: testData/exec/spec/dowstmt.nut ================================================ local a=0 do { println(a) a += 1 } while(a>100) ================================================ FILE: testData/exec/spec/dowstmt.out ================================================ 0 ================================================ FILE: testData/exec/spec/enums.nut ================================================ enum Foo { x = 1, y = 2, z = 3 } println(Foo.x) println(Foo.y) println(Foo.z) ================================================ FILE: testData/exec/spec/enums.out ================================================ 1 2 3 ================================================ FILE: testData/exec/spec/foreachstmt.nut ================================================ function foreach_show(a) { foreach (val in a) println ($"value={val}") foreach (idx, val in a) println($"index={idx} value={val}") } foreach_show([10,23,33,41,589,56]) //tables foreach_show({x = 10, y = 11.5, z = "str", w = true}) //generators function range(n) { for (local i=0; i b) a = b else b = a //// if ( a == 10 ) { b = a + b return a - 10 } ================================================ FILE: testData/exec/spec/ifstmt.out ================================================ ================================================ FILE: testData/exec/spec/sort.nut ================================================ // Basic ascending sort with comparator let a = [4, 2, 5] a.sort(@(a, b) a<=>b) println("Basic ascending:") a.each(@(v) println(v)) // Descending sort let b = [4, 2, 5, 1, 3] b.sort(@(a, b) b<=>a) println("\nDescending:") b.each(@(v) println(v)) // Default sort (no comparator) let c = [8, 3, 9, 1, 5] c.sort() println("\nDefault sort:") c.each(@(v) println(v)) // Sorting strings let d = ["zebra", "apple", "mango", "banana"] d.sort(@(a, b) a<=>b) println("\nString sort:") d.each(@(v) println(v)) // Already sorted array let e = [1, 2, 3, 4, 5] e.sort(@(a, b) a<=>b) println("\nAlready sorted:") e.each(@(v) println(v)) // Array with duplicates let f = [3, 1, 4, 1, 5, 9, 2, 6, 5] f.sort(@(a, b) a<=>b) println("\nWith duplicates:") f.each(@(v) println(v)) // Single element let g = [42] g.sort(@(a, b) a<=>b) println("\nSingle element:") g.each(@(v) println(v)) // Empty array let h = [] h.sort(@(a, b) a<=>b) println("\nEmpty array:") h.each(@(v) println(v)) // Negative numbers let i = [-5, 3, -2, 0, 7, -1] i.sort(@(a, b) a<=>b) println("\nNegative numbers:") i.each(@(v) println(v)) // Floats let j = [3.14, 2.71, 1.41, 2.5] j.sort(@(a, b) a<=>b) println("\nFloats:") j.each(@(v) println(v)) // Custom comparator - sort by absolute value let k = [-5, 3, -2, 0, 7, -1] k.sort(@(a, b) (a < 0 ? -a : a) <=> (b < 0 ? -b : b)) println("\nBy absolute value:") k.each(@(v) println(v)) // Sort array of tables by a field let m = [ {name = "Charlie", age = 25}, {name = "Alice", age = 30}, {name = "Bob", age = 20} ] m.sort(@(a, b) a.age <=> b.age) println("\nTables by age:") m.each(@(v) println(v.name + ": " + v.age)) // Reverse sorted array let n = [9, 7, 5, 3, 1] n.sort(@(a, b) a<=>b) println("\nReverse sorted input:") n.each(@(v) println(v)) // Large array let p = [] for (local x = 10; x >= 1; x--) p.append(x) p.sort(@(a, b) a<=>b) println("\nLarge array (10 elements):") p.each(@(v) println(v)) // Resize is forbidden println("\nTrying to resize during sorting:") try { p.sort(function(a, b) { p.resize(17) return a<=>b }) } catch (e) { println(e) } ================================================ FILE: testData/exec/spec/sort.out ================================================ Basic ascending: 2 4 5 Descending: 5 4 3 2 1 Default sort: 1 3 5 8 9 String sort: apple banana mango zebra Already sorted: 1 2 3 4 5 With duplicates: 1 1 2 3 4 5 5 6 9 Single element: 42 Empty array: Negative numbers: -5 -2 -1 0 3 7 Floats: 1.41 2.5 2.71 3.14 By absolute value: 0 -1 -2 3 -5 7 Tables by age: Bob: 20 Charlie: 25 Alice: 30 Reverse sorted input: 1 3 5 7 9 Large array (10 elements): 1 2 3 4 5 6 7 8 9 10 Trying to resize during sorting: array resized during sort operation ================================================ FILE: testData/exec/spec/stringtmplt.nut ================================================ local foo = 123 print($"\{ foo = {foo} \}") ================================================ FILE: testData/exec/spec/stringtmplt.out ================================================ { foo = 123 } ================================================ FILE: testData/exec/spec/trystmt.nut ================================================ function foo(a, b, c) { try { println($"TRYING {a}") throw b } catch (e) { println($"CAUGHT {e}") } println($"FINISHING {c}") } foo(1, 2, 3) ================================================ FILE: testData/exec/spec/trystmt.out ================================================ TRYING 1 CAUGHT 2 FINISHING 3 ================================================ FILE: testData/exec/spec/whilestmt.nut ================================================ function testy(n) { local a = 0 while (a < n) a += 1 while (1) { if (a < 0) break println(a) a -= 1 } } testy(10) ================================================ FILE: testData/exec/spec/whilestmt.out ================================================ 10 9 8 7 6 5 4 3 2 1 0 ================================================ FILE: testData/exec/stack_metamethod.nut ================================================ local A = class { function _call(...) {} } local a = A() function fn1(n, ...) { local a1, b1, c1, d1 if (n > 0) { a(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, ) fn1(n - 1) } } fn1(10000) ================================================ FILE: testData/exec/stack_metamethod.out ================================================ ================================================ FILE: testData/exec/stack_metamethod_few_args.nut ================================================ local A = class { function _call(...) {} } local a = A() function fn1(n, ...) { if (n > 0) { a(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13) fn1(n - 1) } } fn1(10000) ================================================ FILE: testData/exec/stack_metamethod_few_args.out ================================================ ================================================ FILE: testData/exec/stack_metamethod_locals.nut ================================================ local A = class { function _call(...) { } } local a = A() function fn1(n, ...) { local a000, a001, a002, a003, a004, a005, a006, a007, a008, a009; local a010, a011, a012, a013, a014, a015, a016, a017, a018, a019; local a020, a021, a022, a023, a024, a025, a026, a027, a028, a029; local a030, a031, a032, a033, a034, a035, a036, a037, a038, a039; local a040, a041, a042, a043, a044, a045, a046, a047, a048, a049; local a050, a051, a052, a053, a054, a055, a056, a057, a058, a059; local a060, a061, a062, a063, a064, a065, a066, a067, a068, a069; local a070, a071, a072, a073, a074, a075, a076, a077, a078, a079; local a080, a081, a082, a083, a084, a085, a086, a087, a088, a089; local a090, a091, a092, a093, a094, a095, a096, a097, a098, a099; local a100, a101, a102, a103, a104, a105, a106, a107, a108, a109; local a110, a111, a112, a113, a114, a115, a116, a117, a118, a119; local a120, a121, a122, a123, a124, a125, a126, a127, a128, a129; local a130, a131, a132, a133, a134, a135, a136, a137, a138, a139; local a140, a141, a142, a143, a144, a145, a146, a147, a148, a149; local a150, a151, a152, a153, a154, a155, a156, a157, a158, a159; local a160, a161, a162, a163, a164, a165, a166, a167, a168, a169; local a170, a171, a172, a173, a174, a175, a176, a177, a178, a179; local a180, a181, a182, a183, a184, a185, a186, a187, a188, a189; if (n > 0) { a(1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0) fn1(n - 1, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0) } } try { fn1(500) fn1(500) println("OK") } catch (e) { println("Exception:", e) } ================================================ FILE: testData/exec/stack_metamethod_locals.out ================================================ OK ================================================ FILE: testData/exec/staticmemo/no_memo_mutable_args.nut ================================================ // Test: file-level 'let' variables holding mutable values must NOT cause // auto-static-memoization when passed to [pure] functions. let config = { value = 10 } // --- pure function returning a primitive derived from mutable state --- function [pure] readValue(t) { return t.value } function getFromConfig() { return readValue(config) } assert(getFromConfig() == 10) config.value = 42 assert(getFromConfig() == 42) println("primitive return: ok") // --- pure function creating a new table from mutable state --- config.value = 100 function [pure] snapshot(t) { return { captured = t.value } } function takeSnapshot() { return snapshot(config) } assert(takeSnapshot().captured == 100) config.value = 200 assert(takeSnapshot().captured == 200) println("new table return: ok") // --- pure function returning its argument (must not freeze it) --- let data = { x = 1 } function [pure] passthrough(t) { return t } function getData() { return passthrough(data) } let d = getData() d.x = 999 assert(d.x == 999) assert(data.x == 999) println("passthrough not frozen: ok") // --- multiple mutable arguments --- let left = { n = 1 } let right = { n = 2 } function [pure] addFields(a, b) { return a.n + b.n } function computeSum() { return addFields(left, right) } assert(computeSum() == 3) left.n = 10 right.n = 20 assert(computeSum() == 30) println("two mutable args: ok") // --- nested pure calls --- config.value = 5 function [pure] double(n) { return n * 2 } function getDoubled() { return double(readValue(config)) } assert(getDoubled() == 10) config.value = 50 assert(getDoubled() == 100) println("nested pure calls: ok") ================================================ FILE: testData/exec/staticmemo/no_memo_mutable_args.out ================================================ primitive return: ok new table return: ok passthrough not frozen: ok two mutable args: ok nested pure calls: ok ================================================ FILE: testData/exec/staticmemo/no_memo_mutable_result.nut ================================================ // Auto-static-memo must not memoize pure functions returning mutable containers let t = freeze({a = 1}) function f() { let v = t.values() v.sort() } f() print("OK\n") ================================================ FILE: testData/exec/staticmemo/no_memo_mutable_result.out ================================================ OK ================================================ FILE: testData/exec/staticmemo/static_assign_itself.nut ================================================ local x = 333 x = static x println(x) local y = {a = 444} y = static y println(y.a) local z = [555, 666] z = static z println(z[1]) local w = [777, 888] w = static w w = static w println(w[1]) local a = 111 a = static a a = static a println(a) ================================================ FILE: testData/exec/staticmemo/static_assign_itself.out ================================================ 333 444 666 888 111 ================================================ FILE: testData/exec/staticmemo/static_exception.nut ================================================ local div = 0 function fn() { try { let tab = static { x = 123 / div } println(tab.x) } catch (e) { println("exception") } } fn() fn() div = 2 fn() fn() ================================================ FILE: testData/exec/staticmemo/static_exception.out ================================================ exception exception 61 61 ================================================ FILE: testData/exec/staticmemo/static_freeze.nut ================================================ let t = static {x = null} try { t.x = "fail" println(t.x) } catch (e) { println("success") } ================================================ FILE: testData/exec/staticmemo/static_freeze.out ================================================ success ================================================ FILE: testData/exec/staticmemo/static_loop.nut ================================================ for (local x = 111; x < 116; x++) println(static x) ================================================ FILE: testData/exec/staticmemo/static_loop.out ================================================ 111 111 111 111 111 ================================================ FILE: testData/exec/staticmemo/static_nested.nut ================================================ for (local x = 111; x < 114; x++) println(static static static static static x) println("") local y = 222 for (local x = y; x < 224; x++) { println(static static ~(static static static ~x + static(x * 2) + static x)) println(~(~y + (y * 2) + y)) } ================================================ FILE: testData/exec/staticmemo/static_nested.out ================================================ 111 111 111 -444 -444 -444 -444 ================================================ FILE: testData/exec/staticmemo/static_opt.nut ================================================ function fn() { println(static (1 + 2 + 4 + 8)) println(static (1 | 2 | 4 | 8)) let s = "===" println(s) } fn() fn() ================================================ FILE: testData/exec/staticmemo/static_opt.out ================================================ 15 15 === 15 15 === ================================================ FILE: testData/exec/staticmemo/static_opt2.nut ================================================ let fni2 = @() 335 let A8 = class { function _get(idx) { return idx + -3 }} let ci8 = A8() let fni11 = @() 11 let fni9 = @(p10) p10 - fni11() let t12 = { f0=3 } let A13 = class { function _get(idx) { return idx + 3 }} let ci13 = A13() let t7 = { f0=(((static(ci8[((298) | (10 - -3))]) == 999)) ? (10) : (t12["f0"])) f1=ci13[2] } println(t7.f0) println(t7.f1) ================================================ FILE: testData/exec/staticmemo/static_opt2.out ================================================ 3 5 ================================================ FILE: testData/exec/staticmemo/static_reset1.nut ================================================ let { reset_static_memos } = require("modules") local a, s function sw() { return a } function func() { function xx1() { for (local i = 0; i < 1000; i++) s += (static(sw())) + (static sw()) } function xx2() { for (local i = 0; i < 1000; i++) s += static sw() } s = 0 xx1() xx2() println(s) s = 0 xx1() xx2() println(s) } a = 1 func() reset_static_memos() println("") a = 2 func() ================================================ FILE: testData/exec/staticmemo/static_reset1.out ================================================ 3000 3000 6000 6000 ================================================ FILE: testData/exec/staticmemo/static_reset2.nut ================================================ let { reset_static_memos } = require("modules") local a, s function sw() { return a } function func() { function xx() { s = 0 for (local i = 0; i < 1000; i++) s += static sw() } reset_static_memos() reset_static_memos() a = 1 xx() println(s) reset_static_memos() a = 2 xx() println(s) reset_static_memos() } func() func() ================================================ FILE: testData/exec/staticmemo/static_reset2.out ================================================ 1000 2000 1000 2000 ================================================ FILE: testData/exec/staticmemo/static_reset_module.nut ================================================ function fn(x) { x++ println(static x) x++ println(x) } fn(10) fn(20) require("static_reset1.nut") fn(30) fn(40) ================================================ FILE: testData/exec/staticmemo/static_reset_module.out ================================================ 11 12 11 22 3000 3000 6000 6000 31 32 31 42 ================================================ FILE: testData/exec/staticmemo/static_tables.nut ================================================ local add = 0 function fn() { let x = static {a = 222 + add} println(x.a) let y = static({a = 222 + add}) println(y.a) let z = static {a = 222 + add}.__update({a = 333 + add}) println(z.a) let w = static {a = 222 + add}.__update(static {a = 333 + add}) println(z.a) println(""); } fn() add = 1 fn() ================================================ FILE: testData/exec/staticmemo/static_tables.out ================================================ 222 222 333 333 222 222 333 333 ================================================ FILE: testData/exec/staticmemo/static_types.nut ================================================ class A { function fn() { return 444 } } let a = A() println(static null) println(static true) println(static 123) println(static 123.25) println(static "asdf") println((static [678])[0]) println((static {a = 987})["a"]) println("closure" == typeof static println) println(static println("print")) println(typeof static A) println(typeof static a) println(typeof static a.fn) function t() { println(static a.fn()) } t() t() ================================================ FILE: testData/exec/staticmemo/static_types.out ================================================ null true 123 123.25 asdf 678 987 false print null class instance function 444 444 ================================================ FILE: testData/exec/stdlib/blob_methods.nut ================================================ let { blob, casti2f, castf2i, swap2, swap4, swapfloat } = require("iostream") let b = blob(16) assert(b.len() == 16) assert(typeof b == "blob") b.resize(4) assert(b.len() == 4) b[0] = 0xAA b[1] = 0xBB b[2] = 0xCC b[3] = 0xDD assert(b[0] == 0xAA) assert(b[1] == 0xBB) assert(b[2] == 0xCC) assert(b[3] == 0xDD) let s = blob(4) s[0] = 0x12 s[1] = 0x34 s[2] = 0x56 s[3] = 0x78 s.swap2() assert(s[0] == 0x34 && s[1] == 0x12 && s[2] == 0x78 && s[3] == 0x56) let q = blob(4) q[0] = 0x11; q[1] = 0x22; q[2] = 0x33; q[3] = 0x44 q.swap4() assert(q[0] == 0x44 && q[1] == 0x33 && q[2] == 0x22 && q[3] == 0x11) let t = blob(5) t[0] = 104 t[1] = 101 t[2] = 108 t[3] = 108 t[4] = 111 assert(t.as_string() == "hello") let bi = blob(3) bi[0] = 10; bi[1] = 20; bi[2] = 30 local sum = 0 foreach (_, v in bi) { sum += v } assert(sum == 60) let orig = blob(3) orig[0] = 1; orig[1] = 2; orig[2] = 3 let cp = clone orig assert(cp.len() == 3) assert(cp[0] == 1 && cp[1] == 2 && cp[2] == 3) cp[0] = 99 assert(orig[0] == 1) let i = 0x3f800000 let f = casti2f(i) assert(typeof f == "float") let ii = castf2i(f) assert(ii == i) assert(swap2(0x1234) == 0x3412) assert(swap4(0x11223344) == 0x44332211) let sf = swapfloat(1.0) assert(typeof sf == "float") function mustThrow(label, fn) { try { fn(); println($"FAIL: {label} accepted null") } catch (_) {} } let zz = blob(4) mustThrow("blob.ctor", @() blob(null)) mustThrow("resize", @() zz.resize(null)) mustThrow("_set.1", @() zz._set(null, 0)) mustThrow("_set.2", @() zz._set(0, null)) mustThrow("_get", @() zz._get(null)) mustThrow("_cloned", @() zz._cloned(null)) mustThrow("casti2f", @() casti2f(null)) mustThrow("castf2i", @() castf2i(null)) mustThrow("g.swap2", @() swap2(null)) mustThrow("g.swap4", @() swap4(null)) mustThrow("g.swapfloat", @() swapfloat(null)) println("ok") ================================================ FILE: testData/exec/stdlib/blob_methods.out ================================================ ok ================================================ FILE: testData/exec/stdlib/copy_content_with_replace.nut ================================================ // Arrays let a = [1, 2, 3, 4] let b = [5, 6, 7] let aRef = a.replace_with(b) b[0] = 999 assert(aRef == a) assert(a[0]==5) println("== a ==") foreach (x in a) println(x) println("== aRef ==") foreach (x in aRef) println(x) println("== b ==") foreach (x in b) println(x) // Tables let p = {x=123, y=456} let q = {z=777} let pRef = p.replace_with(q) q.z = "foo" assert(pRef == p) assert(p.z == 777) assert("x" not in p) println("== p ==") foreach (k, v in p) println(k, "=", v) println("== pRef ==") foreach (k, v in pRef) println(k, "=", v) println("== q ==") foreach (k, v in q) println(k, "=", v) ================================================ FILE: testData/exec/stdlib/copy_content_with_replace.out ================================================ == a == 5 6 7 == aRef == 5 6 7 == b == 999 6 7 == p == z = 777 == pRef == z = 777 == q == z = foo ================================================ FILE: testData/exec/stdlib/datetime.nut ================================================ let { clock, time, date } = require("datetime") assert(typeof clock() == "float") assert(clock() >= 0.0) assert(typeof time() == "integer") assert(time() > 0) let d = date() assert(typeof d == "table") assert("sec" in d) assert("min" in d) assert("hour" in d) assert("day" in d) assert("month" in d) assert("year" in d) assert("wday" in d) assert("yday" in d) assert(d.sec >= 0 && d.sec <= 60) assert(d.min >= 0 && d.min <= 59) assert(d.hour >= 0 && d.hour <= 23) assert(d.day >= 1 && d.day <= 31) assert(d.month >= 0 && d.month <= 11) assert(d.year >= 2020) assert(d.wday >= 0 && d.wday <= 6) assert(d.yday >= 0 && d.yday <= 365) let t = time() let dLocal = date(t, 'l') let dUtc = date(t, 'u') assert(typeof dLocal == "table") assert(typeof dUtc == "table") assert(dLocal.year >= 2020) assert(dUtc.year >= 2020) function mustThrow(label, fn) { try { fn(); println($"FAIL: {label} accepted null") } catch (_) {} } mustThrow("date.1", @() date(null)) mustThrow("date.2", @() date(t, null)) println("ok") ================================================ FILE: testData/exec/stdlib/datetime.out ================================================ ok ================================================ FILE: testData/exec/stdlib/debug.nut ================================================ let dbg = require("debug") let info = dbg.getbuildinfo() println($"Version: {info.version}") println($"seterrorhandler: {type(dbg.seterrorhandler)}") println($"setdebughook: {type(dbg.setdebughook)}") println($"getstackinfos: {type(dbg.getstackinfos)}") println($"error: {type(error)}") println($"errorln: {type(errorln)}") if (info.gc == "enabled") { if (type(dbg.collectgarbage) == "function") { println($"collectgarbage: OK") } else { println($"collectgarbage: FAIL") } if (type(dbg.resurrectunreachable) == "function") { println($"resurrectunreachable: OK") } else { println($"resurrectunreachable: FAIL") } } else { if (dbg?.collectgarbage == null) { println($"collectgarbage: OK") } else { println($"collectgarbage: FAIL") } if (dbg?.resurrectunreachable == null) { println($"resurrectunreachable: OK") } else { println($"resurrectunreachable: FAIL") } } println($"getobjflags: {type(getobjflags)}") println($"getbuildinfo: {type(dbg.getbuildinfo)}") function fn(x: int, y: float): float { return x * y } println($"get_function_decl_string: {dbg.get_function_decl_string(fn)}") println($"type_mask_to_string(6) = {dbg.type_mask_to_string(6)}") ================================================ FILE: testData/exec/stdlib/debug.out ================================================ Version: 4.20.0 seterrorhandler: function setdebughook: function getstackinfos: function error: function errorln: function collectgarbage: OK resurrectunreachable: OK getobjflags: function getbuildinfo: function get_function_decl_string: pure (table|userdata|instance|class|null).fn(x: int, y: float): float type_mask_to_string(6) = number ================================================ FILE: testData/exec/stdlib/debug_extras.nut ================================================ let debugLib = require("debug") let { getlocals, format_call_stack_string, script_watchdog_kick, set_script_watchdog_timeout_msec, get_function_info_table, seterrorhandler, setdebughook } = debugLib function testLocals() { local a = 1 local b = "hello" local c = [1, 2, 3] let locals = getlocals(1) assert(typeof locals == "table") assert(locals.a == 1) assert(locals.b == "hello") assert(locals.c.len() == 3) } testLocals() let prev = set_script_watchdog_timeout_msec(1000) assert(typeof prev == "integer") script_watchdog_kick() set_script_watchdog_timeout_msec(prev) function targetFn(x: int, y: int): int { return x + y } let info = get_function_info_table(targetFn) assert(typeof info == "table") assert(info.functionName == "targetFn") assert(info.native == false) assert(typeof info.requiredArgs == "integer") let s = format_call_stack_string() assert(typeof s == "string") assert(s.len() > 0) function myErrorHandler(_err) {} seterrorhandler(myErrorHandler) seterrorhandler(null) function myDebugHook(_event, _src, _line, _name) {} setdebughook(myDebugHook) setdebughook(null) try { seterrorhandler("bad") println("FAIL: seterrorhandler accepted string") } catch (e) { assert(typeof e == "string") } try { setdebughook(42) println("FAIL: setdebughook accepted int") } catch (e) { assert(typeof e == "string") } function mustThrow(label, fn) { try { fn(); println($"FAIL: {label} accepted null") } catch (_) {} } mustThrow("getstackinfos", @() debugLib.getstackinfos(null)) mustThrow("getlocals.1", @() getlocals(null)) mustThrow("getlocals.2", @() getlocals(1, null)) mustThrow("set_script_watchdog_timeout_msec", @() set_script_watchdog_timeout_msec(null)) mustThrow("get_function_decl_string", @() debugLib.get_function_decl_string(null)) mustThrow("type_mask_to_string", @() debugLib.type_mask_to_string(null)) mustThrow("get_function_info_table", @() get_function_info_table(null)) mustThrow("doc", @() debugLib.doc(null)) println("ok") ================================================ FILE: testData/exec/stdlib/debug_extras.out ================================================ ok ================================================ FILE: testData/exec/stdlib/deep_hash.nut ================================================ from "math" import * from "string" import * class A { w = 333 } let inst1 = A() let inst2 = A() assert(deep_hash({}) == deep_hash({})) assert(deep_hash({x = 3}) == deep_hash({x = 3})) assert(deep_hash({x = {y = 444}}, 2) == deep_hash({x = {y = 111}}, 2)) assert(deep_hash({xxx = {y = 444}}, 2) != deep_hash({qqq = {y = 444}}, 2)) assert(deep_hash([{x = 3}, null, 0.75]) == deep_hash([{x = 3}, null, 0.75])) assert(deep_hash([{x = 3}, null, 0.75]) != deep_hash([{x = 4}, null, 0.75])) assert(deep_hash([1, 2, 3]) == deep_hash([1, 2, 3])) assert(deep_hash([1, 2, 3]) != deep_hash([3, 2, 1])) assert(deep_hash(inst1) == deep_hash(inst2)) inst2.w = 999 assert(deep_hash(inst1) != deep_hash(inst2)) assert(hash("a123") == deep_hash("a123")) assert(hash("a123") != deep_hash("XXX")) assert(hash("a123") == hash("a123")) assert(hash("a123") != hash("XXX")) assert(hash(0) != hash(false)) assert(hash(0) != hash(0.0)) assert(hash(-0.0) != hash(0.0)) assert(hash("") != hash(0)) assert(hash("") != hash([])) assert(hash({}) != hash([])) assert(hash(0) > 100000) assert(hash(null) > 100000) assert(hash(0.0) > 100000) assert(hash("") > 100000) assert(hash({}) > 100000) assert(hash([]) > 100000) assert(hash(class {}) > 100000) local arr = [] local cnt = 1000 while (cnt--) { arr.append(cnt) assert(deep_hash(arr) > 0) } println("ok") ================================================ FILE: testData/exec/stdlib/deep_hash.out ================================================ ok ================================================ FILE: testData/exec/stdlib/deep_regex.nut ================================================ // Test: Regex matching with deeply nested repetitions // Tries to overflow stack during match execution let { regexp } = require("string") // Deep alternation chains that create recursive match paths print("Test 1: nested groups with +\n") local depths = [1, 10, 100, 300, 600, 900, 1900, 2800, 5000, 10000, 50000] foreach (depth in depths) { local pattern = "" for (local i = 0; i < depth; i++) pattern += "(" pattern += "a+" for (local i = 0; i < depth; i++) pattern += ")" print(" depth " + depth + "... ") try { local r = regexp(pattern) local res = r.search("aaa") print("OK\n") } catch(e) { print("ERROR: " + e + "\n") break } } // Test 2: Long alternation creating wide regex tree print("Test 2: long alternation\n") local alt_counts = [1, 10, 100, 300, 600, 900, 1900, 2800, 5000, 10000, 50000] foreach (count in alt_counts) { local pattern = "" for (local i = 0; i < count; i++) { if (i > 0) pattern += "|" pattern += "a" } print(" alternatives " + count + "... ") try { local r = regexp(pattern) local res = r.search("a") print("OK\n") } catch(e) { print("ERROR: " + e + "\n") break } } // Test 3: Repetition operator with long input print("Test 3: long match\n") local lens = [1, 10, 100, 300, 600, 900, 1900, 2800, 5000, 10000, 50000] foreach (len in lens) { local str = "" for (local i = 0; i < len; i++) str += "a" print(" len " + len + "... ") try { local r = regexp(@"a+") local res = r.search(str) print("OK\n") } catch(e) { print("ERROR: " + e + "\n") break } } print("ALL DONE\n") ================================================ FILE: testData/exec/stdlib/deep_regex.out ================================================ Test 1: nested groups with + depth 1... OK depth 10... OK depth 100... OK depth 300... ERROR: pattern exceeds maximum allowed nesting depth Test 2: long alternation alternatives 1... OK alternatives 10... OK alternatives 100... OK alternatives 300... OK alternatives 600... OK alternatives 900... OK alternatives 1900... ERROR: pattern too complex Test 3: long match len 1... OK len 10... OK len 100... OK len 300... OK len 600... OK len 900... OK len 1900... OK len 2800... OK len 5000... OK len 10000... OK len 50000... OK ALL DONE ================================================ FILE: testData/exec/stdlib/delegates.nut ================================================ from "string" import * print("=== STRING METHODS TESTS ===\n") let s = "Hello, World!" println("len: " + s.len()) // 13 println("tostring: '" + s.tostring() + "'") try println("tointeger (invalid): " + s.tointeger()) catch(e) println(e) try println("tofloat (invalid): " + s.tofloat()) catch(e) println(e) println("hash: " + s.hash()) println("tolower: '" + s.tolower() + "'") println("toupper: '" + s.toupper() + "'") println("tolower partial: '" + s.tolower(0, 5) + "'") println("toupper partial: '" + s.toupper(7, 12) + "'") println("indexof 'World': " + s.indexof("World")) println("indexof 'o' from 5: " + s.indexof("o", 5)) println("contains 'World': " + s.contains("World")) println("contains 'world' (case-sensitive): " + s.contains("world")) println("hasindex 0: " + s.hasindex(0)) println("hasindex 100: " + s.hasindex(100)) println("startswith 'Hello': " + s.startswith("Hello")) println("endswith 'World!': " + s.endswith("World!")) println("slice(0,5): '" + s.slice(0, 5) + "'") println("slice(7): '" + s.slice(7) + "'") println("replace 'World' -> 'Squirrel': '" + s.replace("World", "Squirrel") + "'") let parts = s.split(", ") println("split: [" + "|".join(parts) + "]") println("join: '" + " ".join(["Hello", "Squirrel", "World"]) + "'") println("concat: '" + "".concat("a", "b", "c") + "'") println("strip: '" + " test ".strip() + "'") println("lstrip: '" + " test ".lstrip() + "'") println("rstrip: '" + " test ".rstrip() + "'") println("escape: '" + "\n\t\"\'\\".escape() + "'") println("subst: '" + "Hello {0}, {1}!".subst("Squirrel", "World") + "'") println("format: '" + format("Value: %d", 42) + "'") printf("printf test: %s %d\n", "value", 100) let chars = "a,b:c;d".split_by_chars(",;:", false) println("split_by_chars: [" + "|".join(chars) + "]") let s2 = clone s println("clone equality: " + (s == s2)) print("\n=== NUMERIC METHODS TESTS ===\n") let i = 42 println("integer.tostring: '" + i.tostring() + "'") println("integer.tointeger: " + i.tointeger()) println("integer.tofloat: " + i.tofloat()) println("integer.tochar: '" + i.tochar() + "'") // '*' println("integer.clone: " + clone(i)) let f = 3.14 println("float.tostring: '" + f.tostring() + "'") println("float.tointeger: " + f.tointeger()) println("float.tofloat: " + f.tofloat()) println("float.clone: " + clone(f)) { let b = true println("bool.tostring: '" + b.tostring() + "'") println("bool.tointeger: " + b.tointeger()) println("bool.tofloat: " + b.tofloat()) println("bool.tochar: '" + b.tochar() + "'") // '\x01' println("bool.clone: " + clone(b)) } print("\n=== CLOSURE METHODS TESTS ===\n") local free_var = "captured" local closure = @(x) x + " " + free_var println("closure.call: '" + closure.call(this, "test") + "'") println("closure.pcall: " + closure.pcall(this, "safe").tostring()) local err_closure = function err_closure() { throw "error" } local res = "X" try res = err_closure.pcall(this) catch (e) println(e) println("closure.pcall error: " + (res.len() == 2 ? res[1] : "success")) local finfo = closure.getfuncinfos() println("closure.getfuncinfos keys: " + finfo.rawin("name") + ", " + finfo.rawin("nclosures")) local fvinfo = closure.getfreevar(0) println("closure.getfreevar name: '" + fvinfo.name + "', value: '" + fvinfo.value + "'") local env = { prefix = "env:" } local bound = closure.bindenv(env) println("closure.clone type: " + typeof(clone(closure))) print("\n=== CLASS & INSTANCE METHODS TESTS ===\n") class TestClass { value = null constructor(x) { this.value = x } function method() { return this.value * 2 } } println("class.rawget 'method' exists: " + TestClass.rawin("method")) TestClass.rawset("dynamic", 100) println("class.rawget dynamic: " + TestClass.rawget("dynamic")) println("class.getbase: " + (TestClass.getbase() == null ? "null" : "not null")) println("class.getfuncinfos: " + typeof(TestClass.getfuncinfos())) println("class.hasindex 'value': " + TestClass.hasindex("value")) println("class.is_frozen initially: " + TestClass.is_frozen()) TestClass.lock() println("class.is_frozen after freeze: " + TestClass.is_frozen()) local inst = TestClass(21) println("instance.rawget 'value': " + inst.rawget("value")) inst.rawset("value", 42) println("instance.rawget after set: " + inst.rawget("value")) println("instance.rawin 'method': " + inst.rawin("method")) println("instance.getfuncinfos type: " + typeof(inst.getfuncinfos())) println("instance.hasindex 0: " + inst.hasindex(0)) // false для объекта println("instance.is_frozen: " + inst.is_frozen()) local inst2 = clone(inst) inst2.value = 100 println("clone isolation (original still 42): " + (inst.value == 42)) { local t = {[1] = "123", [2] = "asd"} t.swap(1, 2) println("swap result: t[1]=" + t[1] + ", t[2]=" + t[2]) } local meta = inst.getmetamethod("_add") println("getmetamethod 'add': " + (meta == null ? "null" : "exists")) print("\n=== ALL TESTS COMPLETED ===\n") ================================================ FILE: testData/exec/stdlib/delegates.out ================================================ === STRING METHODS TESTS === len: 13 tostring: 'Hello, World!' cannot convert the string to integer cannot convert the string to float hash: 4027599776755331851 tolower: 'hello, world!' toupper: 'HELLO, WORLD!' tolower partial: 'hello, World!' toupper partial: 'Hello, WORLD!' indexof 'World': 7 indexof 'o' from 5: 8 contains 'World': true contains 'world' (case-sensitive): false hasindex 0: true hasindex 100: false startswith 'Hello': true endswith 'World!': true slice(0,5): 'Hello' slice(7): 'World!' replace 'World' -> 'Squirrel': 'Hello, Squirrel!' split: [Hello|World!] join: 'Hello Squirrel World' concat: 'abc' strip: 'test' lstrip: 'test ' rstrip: ' test' escape: '\x0a\x09\"\'\\' subst: 'Hello Squirrel, World!' format: 'Value: 42' printf test: value 100 split_by_chars: [a|b|c|d] clone equality: true === NUMERIC METHODS TESTS === integer.tostring: '42' integer.tointeger: 42 integer.tofloat: 42 integer.tochar: '*' integer.clone: 42 float.tostring: '3.14' float.tointeger: 3 float.tofloat: 3.14 float.clone: 3.14 bool.tostring: 'true' bool.tointeger: 1 bool.tofloat: 1 bool.tochar: '' bool.clone: true === CLOSURE METHODS TESTS === closure.call: 'test captured' closure.pcall: safe captured error closure.pcall error: success closure.getfuncinfos keys: true, false closure.getfreevar name: 'free_var', value: 'captured' closure.clone type: function === CLASS & INSTANCE METHODS TESTS === class.rawget 'method' exists: true class.rawget dynamic: 100 class.getbase: null class.getfuncinfos: null class.hasindex 'value': true class.is_frozen initially: false class.is_frozen after freeze: false instance.rawget 'value': 21 instance.rawget after set: 42 instance.rawin 'method': true instance.getfuncinfos type: null instance.hasindex 0: false instance.is_frozen: false clone isolation (original still 42): true swap result: t[1]=asd, t[2]=123 getmetamethod 'add': null === ALL TESTS COMPLETED === ================================================ FILE: testData/exec/stdlib/deser_oom.nut ================================================ let { blob } = require("iostream") local function make_payload(type_byte) { local b = blob() b.writen(0xEA, 'b') // START_MARKER b.writen(type_byte, 'b') // object type + size variant b.writen(-1, 'i') // 0xFFFFFFFF as uint32_t (~4 GB) b.seek(0, 'b') return b } println("VARIANT 1: TP_STRING len=0xFFFFFFFF -> sq_getscratchpad(vm, ~4GB)") try { make_payload(0x43).readobject() } catch(e) { println(" caught: " + e) } println("VARIANT 2: TP_ARRAY size=0xFFFFFFFF -> sq_newarray(vm, ~4GB)") try { make_payload(0x52).readobject() } catch(e) { println(" caught: " + e) } println("VARIANT 3: TP_TABLE size=0xFFFFFFFF -> sq_newtableex(vm, ~4GB)") try { make_payload(0x62).readobject() } catch(e) { println(" caught: " + e) } println("done") ================================================ FILE: testData/exec/stdlib/deser_oom.out ================================================ VARIANT 1: TP_STRING len=0xFFFFFFFF -> sq_getscratchpad(vm, ~4GB) caught: String too large during deserialization VARIANT 2: TP_ARRAY size=0xFFFFFFFF -> sq_newarray(vm, ~4GB) caught: Array too large during deserialization VARIANT 3: TP_TABLE size=0xFFFFFFFF -> sq_newtableex(vm, ~4GB) caught: Table too large during deserialization done ================================================ FILE: testData/exec/stdlib/docstring.nut ================================================ let debug = require("debug") function empty_fn() {} class EmptyClass {} let empty_instance = EmptyClass() let empty_table = {} class A { @@"Class docstring" function x() { return 0; } } let instance = A() function test() { return { @@"Table docstring" x = 4 fn = function() { @@"Function docstring" return 1234 } } } println(debug.doc(empty_fn)) println(debug.doc(EmptyClass)) println(debug.doc(empty_instance)) println(debug.doc(empty_table)) println(debug.doc(A)) println(debug.doc(instance)) println(debug.doc(test())) println(debug.doc(test().fn)) println(debug.doc(debug.doc)) ================================================ FILE: testData/exec/stdlib/docstring.out ================================================ null null null null Class docstring Class docstring Table docstring Function docstring Returns a documentation string for a function, class, or table ================================================ FILE: testData/exec/stdlib/file_io.nut ================================================ let io = require("io") let { file } = io let { remove } = require("system") let f = file("qtest_file_io.txt", "wb+") assert(typeof f == "file") f.close() let f2 = file("qtest_file_io.txt", "rb") assert(typeof f2 == "file") f2.close() remove("qtest_file_io.txt") // userpointer branch: stdout/stdin/stderr are wrapped via sqstd_createfile assert(typeof io.stdout == "file") assert(typeof io.stdin == "file") assert(typeof io.stderr == "file") function mustThrow(label, fn) { try { fn(); println($"FAIL: {label}") } catch (_) {} } mustThrow("file.ctor.path=null", @() file(null, "rb")) mustThrow("file.missing", @() file("__no_such_file_XYZ_123__.txt", "rb")) mustThrow("file.str+int", @() file("qtest_any.txt", 5)) mustThrow("file.str+null", @() file("qtest_any.txt", null)) mustThrow("file.bad_mode.letter", @() file("qtest_bad1.txt", "zzz")) mustThrow("file.bad_mode.empty", @() file("qtest_bad2.txt", "")) mustThrow("file.bad_mode.suffix", @() file("qtest_bad3.txt", "r?")) println("ok") ================================================ FILE: testData/exec/stdlib/file_io.out ================================================ ok ================================================ FILE: testData/exec/stdlib/math_funcs.nut ================================================ let m = require("math") assert(m.sqrt(4.0) == 2.0) assert(m.sqrt(9.0) == 3.0) assert(m.sin(0.0) == 0.0) assert(m.cos(0.0) == 1.0) assert(m.tan(0.0) == 0.0) assert(m.asin(0.0) == 0.0) assert(m.acos(1.0) == 0.0) assert(m.log(1.0) == 0.0) assert(m.log10(1.0) == 0.0) assert(m.log10(100.0) == 2.0) assert(m.atan(0.0) == 0.0) assert(m.atan2(0.0, 1.0) == 0.0) assert(m.pow(2.0, 3.0) == 8.0) assert(m.pow(10.0, 2.0) == 100.0) assert(m.floor(1.7) == 1.0) assert(m.ceil(1.2) == 2.0) assert(m.round(1.5) == 2.0) assert(m.round(1.4) == 1.0) assert(m.exp(0.0) == 1.0) assert(m.fabs(-3.5) == 3.5) assert(m.fabs(3.5) == 3.5) assert(m.abs(-7) == 7) assert(m.abs(7) == 7) assert(m.abs(-3.5) == 3.5) assert(m.asin(2.0) == m.asin(1.0)) assert(m.acos(-2.0) == m.acos(-1.0)) m.srand(12345) let r1 = m.rand() assert(typeof r1 == "integer") assert(r1 >= 0) assert(r1 <= m.RAND_MAX) m.srand(12345) let r1again = m.rand() assert(r1 == r1again) assert(m.PI > 3.14 && m.PI < 3.15) assert(m.RAND_MAX > 0) assert(m.FLT_MAX > 0.0) assert(m.FLT_MIN > 0.0) function mustThrow(label, fn) { try { fn(); println($"FAIL: {label} accepted null") } catch (_) {} } mustThrow("sqrt", @() m.sqrt(null)) mustThrow("sin", @() m.sin(null)) mustThrow("cos", @() m.cos(null)) mustThrow("asin", @() m.asin(null)) mustThrow("acos", @() m.acos(null)) mustThrow("log", @() m.log(null)) mustThrow("log10", @() m.log10(null)) mustThrow("tan", @() m.tan(null)) mustThrow("atan", @() m.atan(null)) mustThrow("atan2.1",@() m.atan2(null, 1.0)) mustThrow("atan2.2",@() m.atan2(1.0, null)) mustThrow("pow.1", @() m.pow(null, 2.0)) mustThrow("pow.2", @() m.pow(2.0, null)) mustThrow("floor", @() m.floor(null)) mustThrow("ceil", @() m.ceil(null)) mustThrow("round", @() m.round(null)) mustThrow("exp", @() m.exp(null)) mustThrow("srand", @() m.srand(null)) mustThrow("fabs", @() m.fabs(null)) mustThrow("abs", @() m.abs(null)) mustThrow("min.1", @() m.min(null, 1)) mustThrow("min.2", @() m.min(1, null)) mustThrow("max.1", @() m.max(null, 1)) mustThrow("max.2", @() m.max(1, null)) mustThrow("clamp.1",@() m.clamp(null, 0, 1)) mustThrow("clamp.2",@() m.clamp(0, null, 1)) mustThrow("clamp.3",@() m.clamp(0, 0, null)) mustThrow("deep_hash.2", @() m.deep_hash({}, null)) println("ok") ================================================ FILE: testData/exec/stdlib/math_funcs.out ================================================ ok ================================================ FILE: testData/exec/stdlib/math_min_max_clamp.nut ================================================ let math = require("math") println($"min(-1, 1, 2, 3, 4, 5, -1000, 6, 7): {math.min(-1, 1, 2, 3, 4, 5, -1000, 6, 7)}") println($"max(-1, 1, 2, 3, 4, 5, -1000, 8, 9): {math.max(-1, 1, 2, 3, 4, 5, -1000, 8, 9)}") println($"clamp(10, -100, 300): {math.clamp(10, -100, 300)}") ================================================ FILE: testData/exec/stdlib/math_min_max_clamp.out ================================================ min(-1, 1, 2, 3, 4, 5, -1000, 6, 7): -1000 max(-1, 1, 2, 3, 4, 5, -1000, 8, 9): 9 clamp(10, -100, 300): 10 ================================================ FILE: testData/exec/stdlib/obj_serialization.nut ================================================ let { blob } = require("iostream") function deep_compare(a, b) { if (type(a) != type(b)) return false if (type(a) == "array") { if (a.len() != b.len()) return false for (local i = 0; i < a.len(); i++) if (!deep_compare(a[i], b[i])) return false return true } else if (type(a) == "table") { if (a.len() != b.len()) return false local keys = [] foreach (k, _ in a) keys.append(k) keys.sort() foreach (_, key in keys) if (!deep_compare(a[key], b[key])) return false return true } else return a == b } let a = {x = 1.0, y = [{}, {z = 5.0}]} let b = {y = [{}, {z = 5.0}], x = 1.0} println($"deep_compare test1: {deep_compare(a, b)}") b.y[1].z = 5 println($"deep_compare test2: {deep_compare(a, b)}") function test(obj, obj_type) { local b = blob() b.writeobject(obj) b.seek(0) let y = b.readobject() println($"test {obj_type}: {deep_compare(obj, y)}") } test(null, "null") test(true, "bool1") test(false, "bool2") test(0, "int0") test(-1, "int1") test(12, "int2") test(128, "int3") test(32767, "int4") test(11132767, "int5") test(0.0, "float0") test(0.5, "float1") test(10.25, "float2") test(-1e30, "float3") test("", "str0") test("a", "str1") test("aaaabc", "str2") test("aaaabcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaabcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaabcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaabcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaabcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaabcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaabcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + "aaaabcaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "str3") test([], "array0") test([222], "array1") test([222, 333, null, -5.0, "www"], "array2") test(["www", "www", "", "", "", "www", null, "www"], "array_str") test({}, "table0") test({xx = "xx"}, "table1") test({[55] = "xx", [0] = 444}, "table2") test({x = {x = {x = {x = [1, "222", 3, {y = {y = {y = [4, null, "333", "222"]}}}, true, 6, false] }}}}, "recursion") try { local c = class {} test(c, "err1") } catch (e) { println(e) } try { local c = class {} test([null, {x = c}], "err1") } catch (e) { println(e) } function testx(x) { try { local c = blob() let y = c.readobject() println(y) } catch (e) { println(e) } return x + 1 } println(testx(110)) ================================================ FILE: testData/exec/stdlib/obj_serialization.out ================================================ deep_compare test1: true deep_compare test2: false test null: true test bool1: true test bool2: true test int0: true test int1: true test int2: true test int3: true test int4: true test int5: true test float0: true test float1: true test float2: true test float3: true test str0: true test str1: true test str2: true test str3: true test array0: true test array1: true test array2: true test array_str: true test table0: true test table1: true test table2: true test recursion: true Unsupported object type for serialization Unsupported object type for serialization Unexpected end of data during deserialization 111 ================================================ FILE: testData/exec/stdlib/obj_serialization_errors.nut ================================================ let { get_stack_top } = require("debug") let { blob } = require("iostream") let refStackTop = get_stack_top() // Test 1: Maximum serialization depth exceeded function testMaxDepth() { local arr = [] local current = arr for (local i = 0; i < 300; i++) { current.append([]) current = current[0] } local b = blob() try { b.writeobject(arr) println("FAIL: Max depth test should have failed") } catch (e) { println("PASS: Max depth test - " + e) } } // Test 2: Unsupported class for serialization function testUnsupportedClass() { class Unsupported { x = 1 } local obj = Unsupported() local b = blob() try { b.writeobject(obj) println("FAIL: Unsupported class test should have failed") } catch (e) { println("PASS: Unsupported class test - " + e) } } // Test 3: Missing __getstate method function testMissingGetState() { class NoGetState { x = 1 } local obj = NoGetState() local b = blob() try { b.writeobject(obj, { NoGetState = NoGetState }) println("FAIL: Missing __getstate test should have failed") } catch (e) { println("PASS: Missing __getstate test - " + e) } } // Test 4: Invalid __getstate method (not a closure) function testInvalidGetState() { class InvalidGetState { x = 1 __getstate = 123 // not a function } local obj = InvalidGetState() local b = blob() try { b.writeobject(obj, { InvalidGetState = InvalidGetState }) println("FAIL: Invalid __getstate test should have failed") } catch (e) { println("PASS: Invalid __getstate test - " + e) } } // Test 5: Invalid available classes (not a table) function testInvalidAvailableClasses() { class ValidClass { x = 1 function __getstate() { return this.x } function __setstate(s) { this.x = s } } local obj = ValidClass() local b = blob() try { b.writeobject(obj, "not a table") println("FAIL: Invalid available classes test should have failed") } catch (e) { println("PASS: Invalid available classes test - " + e) } } // Test 6: Class not found in available classes during deserialization function testClassNotFound() { class ValidClass { x = 1 function __getstate() { return this.x } function __setstate(s) { this.x = s } } local obj = ValidClass() local b = blob() b.writeobject(obj, { ValidClass = ValidClass }) b.seek(0) try { b.readobject({}) // empty available classes println("FAIL: Class not found test should have failed") } catch (e) { println("PASS: Class not found test - " + e) } } // Test 7: Invalid constructor parameters function testInvalidConstructor() { class InvalidConstructor { x = 1 constructor(_a, _b, _c) {} // requires 3 params function __getstate() { return this.x } function __setstate(s) { this.x = s } } local obj = InvalidConstructor(1,2,3) local b = blob() try { b.writeobject(obj, { InvalidConstructor = InvalidConstructor }) b.seek(0) local x = b.readobject() println("FAIL: Invalid constructor test should have failed") } catch (e) { println("PASS: Invalid constructor test - " + e) } } // Test 8: Invalid start/end markers function testInvalidMarkers() { local b = blob() b.writen(1, 'c') // invalid start marker try { b.seek(0) b.readobject({}) println("FAIL: Invalid start marker test should have failed") } catch (e) { println("PASS: Invalid start marker test - " + e) } // Test with valid start but invalid end marker local b2 = blob() b2.writeobject(123) // corrupt the end b2.seek(b.len() - 1) b2.writen(1, 'c') try { b2.seek(0) b2.readobject({}) println("FAIL: Invalid end marker test should have failed") } catch (e) { println("PASS: Invalid end marker test - " + e) } } // Test 9: Unexpected end of data function testUnexpectedEnd() { local b = blob() b.writeobject([1,2,3]) b.resize(b.len() - 2) // truncate last 2 bytes try { b.seek(0) b.readobject({}) println("FAIL: Unexpected end test should have failed") } catch (e) { println("PASS: Unexpected end test - " + e) } } // Run all tests testMaxDepth() testUnsupportedClass() testMissingGetState() testInvalidGetState() testInvalidAvailableClasses() testClassNotFound() testInvalidConstructor() testInvalidMarkers() testUnexpectedEnd() let stackTop = get_stack_top() assert(stackTop == refStackTop) ================================================ FILE: testData/exec/stdlib/obj_serialization_errors.out ================================================ PASS: Max depth test - Maximum serialization depth exceeded PASS: Unsupported class test - Unsupported class for serialization PASS: Missing __getstate test - Instance must have __getstate method for serialization PASS: Invalid __getstate test - Instance method __getstate must be a closure PASS: Invalid available classes test - parameter 2 of 'writeobject' has an invalid type 'string' ; expected: 'null|table' PASS: Class not found test - Class not found in available classes during deserialization PASS: Invalid constructor test - Instance found during deserialization, but available classes not set or not a table PASS: Invalid start marker test - Invalid start marker during deserialization PASS: Invalid end marker test - Invalid start marker during deserialization PASS: Unexpected end test - Unexpected end of data during deserialization ================================================ FILE: testData/exec/stdlib/obj_serialization_errors_arg.nut ================================================ let { get_stack_top } = require("debug") let { blob } = require("iostream") let refStackTop = get_stack_top() function testInvalidAvailableClasses() { class ValidClass { x = 1 function __getstate(_available_classes) { return this.x } function __setstate(s, _available_classes) { this.x = s } } local obj = ValidClass() local b = blob() try { b.writeobject(obj, "not a table") println("FAIL: Invalid available classes test should have failed") } catch (e) { println("PASS: Invalid available classes test - " + e) } } function testClassNotFound() { class ValidClass { x = 1 function __getstate(_available_classes) { return this.x } function __setstate(s, _available_classes) { this.x = s } } local obj = ValidClass() local b = blob() b.writeobject(obj, { ValidClass = ValidClass }) b.seek(0) try { b.readobject({}) // empty available classes println("FAIL: Class not found test should have failed") } catch (e) { println("PASS: Class not found test - " + e) } } function testInvalidConstructor() { class InvalidConstructor { x = 1 constructor(_a, _b, _c) {} // requires 3 params function __getstate(_available_classes) { return this.x } function __setstate(s, _available_classes) { this.x = s } } local obj = InvalidConstructor(1,2,3) local b = blob() try { b.writeobject(obj, { InvalidConstructor = InvalidConstructor }) b.seek(0) local x = b.readobject() println("FAIL: Invalid constructor test should have failed") } catch (e) { println("PASS: Invalid constructor test - " + e) } } testInvalidAvailableClasses() testClassNotFound() testInvalidConstructor() let stackTop = get_stack_top() assert(stackTop == refStackTop) ================================================ FILE: testData/exec/stdlib/obj_serialization_errors_arg.out ================================================ PASS: Invalid available classes test - parameter 2 of 'writeobject' has an invalid type 'string' ; expected: 'null|table' PASS: Class not found test - Class not found in available classes during deserialization PASS: Invalid constructor test - Instance found during deserialization, but available classes not set or not a table ================================================ FILE: testData/exec/stdlib/obj_serialization_valid.nut ================================================ let { get_stack_top } = require("debug") let { blob } = require("iostream") function testValidStringIndex() { // Create an array with multiple strings to test string table references local strings = [ "first string", "second string", "third string" ] local b = blob() b.writeobject(strings) b.seek(0) try { local result = b.readobject({}) // Verify all strings were deserialized correctly if (result.len() != 3) { println("FAIL: Valid string index test - wrong array length") return } if (result[0] != "first string" || result[1] != "second string" || result[2] != "third string") { println("FAIL: Valid string index test - string content mismatch") return } println("PASS: Valid string index test") } catch (e) { println("FAIL: Valid string index test - unexpected error: " + e) } } function testValidClassIndex() { class TestClassA { value = null constructor(v = null) { this.value = v } function __getstate(_available_classes) { return this.value } function __setstate(v) { this.value = v } } class TestClassB { data = null constructor(d = null) { this.data = d } function __getstate() { return this.data } function __setstate(d, _available_classes) { this.data = d } } // Create an array with multiple instances to test class table references local objects = [ TestClassA(100), TestClassB("test"), TestClassA(200), TestClassB("data") ] local b = blob() b.writeobject(objects, { TestClassA = TestClassA, TestClassB = TestClassB }) b.seek(0) try { local result = b.readobject({ TestClassA = TestClassA, TestClassB = TestClassB }) // Verify all objects were deserialized correctly if (result.len() != 4) { println("FAIL: Valid class index test - wrong array length") return } if (result[0].value != 100 || result[1].data != "test" || result[2].value != 200 || result[3].data != "data") { println("FAIL: Valid class index test - object content mismatch") return } println("PASS: Valid class index test") } catch (e) { println("FAIL: Valid class index test - unexpected error: " + e) } } let refStackTop = get_stack_top() // Run the valid tests testValidStringIndex() testValidStringIndex() testValidClassIndex() testValidClassIndex() let stackTop = get_stack_top() assert(refStackTop == stackTop) ================================================ FILE: testData/exec/stdlib/obj_serialization_valid.out ================================================ PASS: Valid string index test PASS: Valid string index test PASS: Valid class index test PASS: Valid class index test ================================================ FILE: testData/exec/stdlib/rawdelete.nut ================================================ for (local size = 1; size < 17; size++) { for (local iter = 0; iter < 2000; iter++) { //println("============") local seed = size + iter * 21 local t = {} local indices = [] for (local i = 0; i < size; i++) { seed++ seed *= 198375 indices.append(seed) t[seed] <- seed } //println($"before delete indices.len() = {indices.len()}") for (local i = 0; i < iter % size; i++) { if (iter & 1) { t.$rawdelete(indices[0]) indices.remove(0) } else { t.$rawdelete(indices[indices.len() - 1]) indices.remove(indices.len() - 1) } } //println($"after delete indices.len() = {indices.len()}") //println($"before ins table.len = {t.len()}") t.x <- "abc" foreach (_, v in indices) assert(t?[v] != null) //println($"after ins table.len = {t.len()}") //println(1 + size - iter % size) assert(t.len() == 1 + size - iter % size) assert(t.x == "abc") } } println("ok") ================================================ FILE: testData/exec/stdlib/rawdelete.out ================================================ ok ================================================ FILE: testData/exec/stdlib/regexp.nut ================================================ let { regexp } = require("string") let r = regexp(@"[a-z,A-Z]*") println(r.match("AxBy")) println(r.match("AB-")) let t = r.search("-AAAA----A", 1) foreach (k, v in t) println($"{k}={v}") let f = r.capture("-AAAA----A", 1) foreach (k, v in f[0]) println($"{k}={v}") println(typeof r) println(r.subexpcount()) ================================================ FILE: testData/exec/stdlib/regexp.out ================================================ true false begin=1 end=5 begin=1 end=5 regexp 1 ================================================ FILE: testData/exec/stdlib/regexp_fixed_bugs.nut ================================================ let { regexp } = require("string") // Bug 1: Empty pattern should error, not crash (heap-buffer-overflow) try { regexp("") println("BUG: empty pattern did not error") } catch(e) { println($"empty pattern error: {e}") } // Bug 2: [A-] should error "unfinished range" (dead range-end check) try { regexp("[A-]") println("BUG: [A-] did not error") } catch(e) { println($"unfinished range error: {e}") } // Bug 3: Large quantifier should error, not silently truncate try { regexp(@"^a{70000}$") println("BUG: large quantifier did not error") } catch(e) { println($"quantifier error: {e}") } // Bug 4: Word boundary correctness at various positions let wb = regexp(@"\bword\b") println(wb.match("word")) println(wb.search("hello word bye") != null) println(wb.match("wordy")) // Bug 5: Trailing backslash in escape should error try { regexp("\\") println("BUG: trailing backslash did not error") } catch(e) { println($"trailing backslash error: {e}") } // Bug 6: \m with incomplete args should error try { regexp("\\m") println("BUG: incomplete \\m did not error") } catch(e) { println($"incomplete \\m error: {e}") } try { regexp("\\m(") println("BUG: incomplete \\m( did not error") } catch(e) { println($"incomplete \\m( error: {e}") } // Bug 7: Unclosed [ should error try { regexp("[abc") println("BUG: unclosed [ did not error") } catch(e) { println($"unclosed bracket error: {e}") } // Bug 8: Search finds matches at non-zero positions let sr = regexp(@"ab") let res = sr.search("xab") println($"search begin={res.begin} end={res.end}") // Bug 9: Character range matching correctness let rng = regexp("[A-Z]") println(rng.match("M")) println(rng.match("a")) // Bug 10: Range validation with escape sequences try { let r = regexp("[A-\\n]") // Should not reach here - A(65) > \n(10) is an invalid range println("BUG: [A-\\n] should be invalid range") } catch(e) { println($"[A-\\n] correctly rejected: {e}") } let r_az = regexp("[a-\\z]") println($"[a-\\z] match m: {r_az.match("m")}") // Bug 11: Pattern complexity limit try { local longpat = "aaaaaaaaaa" // 10 for (local i = 0; i < 12; i++) longpat += longpat // 10 * 2^12 = 40960 longpat += longpat.slice(0, 9040) // 50000 regexp(longpat) println("BUG: 50000 char pattern should error") } catch(e) { println($"long pattern error: {e}") } // Bug 12: Nested zero-width quantifiers terminate let zw = regexp(@"\b{0,3}word") println($"zero-width bounded: {zw.match("word")}") // Bug 14: {n,m} where n > m should error at compile time try { regexp("a{5,2}") println("BUG: a{5,2} should be invalid range") } catch(e) { println($"quantifier min>max error: {e}") } // Bug 15: Large quantifier packing correctness (UB fix for p0 >= 32768) let lq = regexp(@"^a{40000}$") local s40k = "" for (local i = 0; i < 15; i++) s40k += "aaaaaaaaaa" // build in chunks of 10 // s40k = 150 chars, won't match {40000} but should not crash/UB println($"large quantifier no crash: {lq.match(s40k) == false}") ================================================ FILE: testData/exec/stdlib/regexp_fixed_bugs.out ================================================ empty pattern error: letter expected unfinished range error: unfinished range quantifier error: quantifier value too large true true false trailing backslash error: letter expected for argument of escape sequence incomplete \m error: balanced chars expected incomplete \m( error: balanced chars expected unclosed bracket error: unterminated character class search begin=1 end=3 true false [A-\n] correctly rejected: invalid range [a-\z] match m: true long pattern error: pattern exceeds maximum allowed nesting depth zero-width bounded: true quantifier min>max error: invalid quantifier range: min > max large quantifier no crash: true ================================================ FILE: testData/exec/stdlib/stream_methods.nut ================================================ let { blob } = require("iostream") let b = blob() b.writen(-5, 'c') b.writen(250, 'b') b.writen(100, 's') b.writen(200, 'w') b.writen(42, 'i') b.writen(256, 'l') b.writen(3.5, 'f') b.writen(2.75, 'd') let expectedLen = 1 + 1 + 2 + 2 + 4 + 8 + 4 + 8 assert(b.len() == expectedLen) assert(b.tell() == expectedLen) b.seek(0, 'b') assert(b.tell() == 0) b.seek(0, 'e') assert(b.tell() == b.len()) b.seek(-4, 'c') assert(b.tell() == b.len() - 4) b.seek(0) assert(b.tell() == 0) assert(b.eos() == null) b.seek(0, 'e') assert(b.eos() != null) b.seek(0) assert(b.readn('c') == -5) assert(b.readn('b') == 250) assert(b.readn('s') == 100) assert(b.readn('w') == 200) assert(b.readn('i') == 42) assert(b.readn('l') == 256) let ff = b.readn('f') assert(ff == 3.5) let dd = b.readn('d') assert(dd == 2.75) let src = blob() src.writen(0xAA, 'b') src.writen(0xBB, 'b') src.writen(0xCC, 'b') src.seek(0) let dst = blob() dst.writeblob(src) assert(dst.len() == 3) dst.seek(0) let rd = dst.readblob(3) assert(typeof rd == "blob") assert(rd.len() == 3) assert(rd[0] == 0xAA) assert(rd[1] == 0xBB) assert(rd[2] == 0xCC) let sb = blob() sb.writestring("abc") assert(sb.len() == 3) sb.seek(0) assert(sb.readn('b') == 97) assert(sb.readn('b') == 98) assert(sb.readn('b') == 99) b.flush() let nb = blob() nb.writeobject([1, 2, 3], null) nb.seek(0) let nres = nb.readobject(null) assert(nres.len() == 3 && nres[0] == 1) function mustThrow(label, fn) { try { fn(); println($"FAIL: {label} accepted null") } catch (_) {} } let z = blob() z.writen(1, 'b') z.seek(0) mustThrow("readblob", @() z.readblob(null)) mustThrow("readn", @() z.readn(null)) mustThrow("writeblob", @() z.writeblob(null)) mustThrow("writestring", @() z.writestring(null)) mustThrow("writen.1", @() z.writen(null, 'b')) mustThrow("writen.2", @() z.writen(1, null)) mustThrow("seek.1", @() z.seek(null)) mustThrow("seek.2", @() z.seek(0, null)) println("ok") ================================================ FILE: testData/exec/stdlib/stream_methods.out ================================================ ok ================================================ FILE: testData/exec/stdlib/string.nut ================================================ let { format, printf, split_by_chars, startswith, endswith, strip, lstrip, rstrip, escape } = require("string") print(" strip ".strip()) println(";") print(" lstrip ".lstrip()) println(";") print(" rstrip ".rstrip()) println(";") print("") println("abcd".endswith("abcd*")) println("abcd".endswith("cd")) println("abcd".endswith("a")) println("abcd".endswith("")) println("") println("abcd".startswith("*abcd")) println("abcd".startswith("ab")) println("abcd".startswith("d")) println("abcd".startswith("")) println("") println(endswith("abcd", "d")) println(startswith("abcd", "a")) print(strip(" strip ")) println(";") print(lstrip(" lstrip ")) println(";") print(rstrip(" rstrip ")) println(";") println("") println("~\n\"\'\\~".escape()) println(escape("~\n\"\'\\~")) println(format("str:%s; int:%d; hex:%x", "st", 255, 255)) printf("str:%s; int:%d; hex:%x\n", "st", 255, 255) let arr = split_by_chars(":aaa,bb:c:::", ",:", true) foreach(_, v in arr) { print(v) print(";") } println("") let arr2 = ":aaa,bb:c:::".split_by_chars(",:", true) foreach(_, v in arr2) { print(v) print(";") } ================================================ FILE: testData/exec/stdlib/string.out ================================================ strip; lstrip ; rstrip; false true false true false true false true true true strip; lstrip ; rstrip; ~\x0a\"\'\\~ ~\x0a\"\'\\~ str:st; int:255; hex:ff str:st; int:255; hex:ff aaa;bb;c; aaa;bb;c; ================================================ FILE: testData/exec/stdlib/string_escape.nut ================================================ let { escape } = require("string") let bs = "\\" // single backslash let dq = "\"" // double-quote let sq = "'" // single-quote // --- empty string: returned unchanged --- assert(escape("") == "") assert("".escape() == "") // --- both call forms produce identical results --- assert("hello".escape() == escape("hello")) // --- all-printable, no special chars: pass through unchanged --- assert("hello".escape() == "hello") assert("Hello World!".escape() == "Hello World!") assert("0123456789".escape() == "0123456789") assert("AaBbCcZz".escape() == "AaBbCcZz") assert(" !#$%&()*+,-./:;<=>?@[]^_`{|}~".escape() == " !#$%&()*+,-./:;<=>?@[]^_`{|}~") // --- named escape chars: backslash, double-quote, single-quote --- assert(bs.escape() == bs + bs) assert(dq.escape() == bs + dq) assert(sq.escape() == bs + sq) assert((bs + dq + sq).escape() == bs + bs + bs + dq + bs + sq) // --- control chars: \a \b \t \n \v \f \r all go to \xNN path --- // (the named-escape switch cases for these are dead code — they live inside // the sq_isprint() branch which is never entered for control chars) assert("\x01".escape() == "\\x01") assert("\x06".escape() == "\\x06") assert("\x07".escape() == "\\x07") // \a assert("\x08".escape() == "\\x08") // \b assert("\x09".escape() == "\\x09") // \t assert("\x0a".escape() == "\\x0a") // \n assert("\x0b".escape() == "\\x0b") // \v assert("\x0c".escape() == "\\x0c") // \f assert("\x0d".escape() == "\\x0d") // \r assert("\x0e".escape() == "\\x0e") assert("\x1f".escape() == "\\x1f") // --- 0x7f (DEL) --- assert("\x7f".escape() == "\\x7f") // --- high bytes 0x80..0xff --- assert("\x80".escape() == "\\x80") assert("\xab".escape() == "\\xab") assert("\xff".escape() == "\\xff") // --- mixed: printable + control + special --- assert("a\x01b".escape() == "a\\x01b") assert("a\nb".escape() == "a\\x0ab") assert(("a" + bs + "b").escape() == "a\\\\b") assert(("a" + dq + "b").escape() == "a\\\"b") // from existing string.nut (extended): ~ LF " ' \ ~ // expected: ~\x0a\"\'\\~ (12 chars) let mixed_escaped = ("~\n" + dq + sq + bs + "~").escape() assert(mixed_escaped.len() == 12) assert(mixed_escaped[ 0] == '~') // literal ~ assert(mixed_escaped[ 1] == '\\') // start of \x0a assert(mixed_escaped[ 2] == 'x') assert(mixed_escaped[ 3] == '0') assert(mixed_escaped[ 4] == 'a') assert(mixed_escaped[ 5] == '\\') // start of \" assert(mixed_escaped[ 6] == '"') assert(mixed_escaped[ 7] == '\\') // start of \' assert(mixed_escaped[ 8] == '\'') assert(mixed_escaped[ 9] == '\\') // start of \\ assert(mixed_escaped[10] == '\\') assert(mixed_escaped[11] == '~') // literal ~ // --- output length is always >= input length --- assert("abc".escape().len() >= 3) assert("\x01\x02\x03".escape().len() >= 3) // --- regression: all-non-printable strings (every char -> 4-byte \xNN) --- // Before the fix, scsprintf received the total buffer size (destcharsize) rather // than the remaining bytes, causing a 1-byte null-terminator OOB write when // every character in the input required hex escaping. assert("\x01".escape() == "\\x01") assert("\x01".escape().len() == 4) assert("\x01\x02".escape() == "\\x01\\x02") assert("\x01\x02".escape().len() == 8) assert("\x01\x02\x03\x04\x05".escape().len() == 20) assert("\x01\x02\x03\x04\x05\x06\x07\x08\x0e\x0f".escape().len() == 40) // larger all-non-printable string to stress the fixed boundary local s50 = "" for (local i = 0; i < 5; i++) s50 += "\x01\x02\x03\x04\x05\x06\x07\x08\x0e\x0f" assert(s50.len() == 50) assert(s50.escape().len() == 200) local s100 = s50 + s50 assert(s100.escape().len() == 400) println("ok") ================================================ FILE: testData/exec/stdlib/string_escape.out ================================================ ok ================================================ FILE: testData/exec/stdlib/string_format.nut ================================================ let str = require("string") let { format } = str assert(format("%d", 42) == "42") assert(format("%d", -5) == "-5") assert(format("%i", 42) == "42") assert(format("%o", 8) == "10") assert(format("%u", 100) == "100") assert(format("%x", 255) == "ff") assert(format("%X", 255) == "FF") assert(format("%c", 65) == "A") assert(format("%c", 97) == "a") assert(format("%s", "hi") == "hi") assert(format("%5d|", 42) == " 42|") assert(format("%-5d|", 42) == "42 |") assert(format("%05d", 42) == "00042") assert(format("%+d", 42) == "+42") assert(format("%+d", -42) == "-42") assert(format("%f", 1.5).indexof("1.5") != null) assert(format("%.2f", 1.5) == "1.50") assert(format("%g", 1.5).indexof("1.5") != null) assert(format("%G", 1.5).indexof("1.5") != null) assert(format("%e", 1000.0).indexof("e") != null) assert(format("%E", 1000.0).indexof("E") != null) assert(format("%%") == "%") assert(format("%s=%d", "n", 7) == "n=7") assert(format("%s-%s", "a", "b") == "a-b") assert(format("%#x", 255) == "0xff") assert(format("% d", 42) == " 42") function mustThrow(label, fn) { try { fn(); println($"FAIL: {label} accepted null") } catch (_) {} } mustThrow("format.fmt", @() format(null)) mustThrow("printf.fmt", @() str.printf(null)) mustThrow("strip", @() str.strip(null)) mustThrow("lstrip", @() str.lstrip(null)) mustThrow("rstrip", @() str.rstrip(null)) mustThrow("escape", @() str.escape(null)) mustThrow("startswith.1", @() str.startswith(null, "a")) mustThrow("startswith.2", @() str.startswith("abc", null)) mustThrow("endswith.1", @() str.endswith(null, "a")) mustThrow("endswith.2", @() str.endswith("abc", null)) mustThrow("split.1", @() str.split_by_chars(null, ",")) mustThrow("split.2", @() str.split_by_chars("a,b", null)) mustThrow("split.3", @() str.split_by_chars("a,b", ",", null)) mustThrow("regexp.ctor", @() str.regexp(null)) let rex = str.regexp("a") mustThrow("regexp.match", @() rex.match(null)) mustThrow("regexp.search.1", @() rex.search(null)) mustThrow("regexp.search.2", @() rex.search("abc", null)) mustThrow("regexp.capture.1", @() rex.capture(null)) mustThrow("regexp.capture.2", @() rex.capture("abc", null)) println("ok") ================================================ FILE: testData/exec/stdlib/string_format.out ================================================ ok ================================================ FILE: testData/exec/stdlib/swap.nut ================================================ class D { x = 1 y = 2 } local d = D() local t = {"1": "123", "2": "asd"} local a = [123, 456, 789] d.swap("x", "y") a.swap(1, 2) t.swap("1", "2") println($"{d["x"]} {d["y"]}") println($"{t["1"]} {t["2"]}") println($"{a[1]} {a[2]}") ================================================ FILE: testData/exec/stdlib/swap.out ================================================ 2 1 asd 123 789 456 ================================================ FILE: testData/exec/stdlib/swap_stack_check.nut ================================================ class D { x = 1 y = 2 } local d = D() local t = {"1": "123", "2": "asd"} local a = [123, 456, 789] function fn(n) { d.swap("x", "y") local x = n - 1 t.swap("1", "2") a.swap(1, 2) if (n > 0) fn(x) println(x) } fn(10) println($"{d["x"]} {d["y"]}") println($"{t["1"]} {t["2"]}") println($"{a[1]} {a[2]}") ================================================ FILE: testData/exec/stdlib/swap_stack_check.out ================================================ -1 0 1 2 3 4 5 6 7 8 9 2 1 asd 123 789 456 ================================================ FILE: testData/exec/stdlib/system_lib.nut ================================================ let { getenv, setenv, system, remove, rename } = require("system") let { file } = require("io") setenv("QUIRREL_STDLIB_TEST_VAR", "hello42") assert(getenv("QUIRREL_STDLIB_TEST_VAR") == "hello42") setenv("QUIRREL_STDLIB_TEST_VAR", "updated") assert(getenv("QUIRREL_STDLIB_TEST_VAR") == "updated") let rc = system("cd .") assert(typeof rc == "integer") let f = file("qtest_sys_tmp.txt", "wb") assert(typeof f == "file") f.close() rename("qtest_sys_tmp.txt", "qtest_sys_tmp2.txt") remove("qtest_sys_tmp2.txt") function mustThrow(label, fn) { try { fn(); println($"FAIL: {label} accepted null") } catch (_) {} } mustThrow("getenv", @() getenv(null)) mustThrow("setenv.1", @() setenv(null, "v")) mustThrow("setenv.2", @() setenv("k", null)) mustThrow("system", @() system(null)) mustThrow("remove", @() remove(null)) mustThrow("rename.1", @() rename(null, "x")) mustThrow("rename.2", @() rename("x", null)) println("ok") ================================================ FILE: testData/exec/stdlib/system_lib.out ================================================ ok ================================================ FILE: testData/exec/string_interpolation.nut ================================================ let x = {y = 123} assert($"" == "") assert($"\{\}" == "{}") assert($"{3}" == "3") assert($"\\{0}\\{1}" == "\\0\\1") assert($"{3}{4}" == "34") assert($"{"3"}{"4"}" == "34") assert($"abc{123}def" == "abc123def") assert($"abc{ 123}def" == "abc123def") assert($"abc{ 123 - 3 }def" == "abc120def") assert($"abc{x.y}\u20" == "abc123 ") assert($"{x["\u79"] == 123}" == "true") assert($"'" == "'") assert($"\\\\" == "\\\\") assert($"\t\n" == "\t\n") assert($"/" == "/") assert($"\}{{s="qwe"}["s"]}\{" == "}qwe{") ================================================ FILE: testData/exec/string_interpolation.out ================================================ ================================================ FILE: testData/exec/string_interpolation_new.nut ================================================ function foo() { return "foo" } function bar(p) { return $"[bar {p}]" } let s = $"x1 = {foo()}, x2 = {bar($"qux = {foo()}")}" print("#1: ") println(s) print("#2: ") println($"{$"{2}"}"); print("#3: ") local gg = 123 println($"\{ gg = {gg} \}") print("#4: ") println($" foo bar ") print("#5: ") println($"{foo()}}}}}}") ================================================ FILE: testData/exec/string_interpolation_new.out ================================================ #1: x1 = foo, x2 = [bar qux = foo] #2: 2 #3: { gg = 123 } #4: foo bar #5: foo}}}}} ================================================ FILE: testData/exec/string_methods.nut ================================================ let s = "Hello, World!" let Exception = class{ e = null constructor(e){ this.e=e } } function test(func, expected_res, name = null){ let name_s = name ? $"'{name}': " : "" try{ let res = func() assert(expected_res==res, @() $"{name_s}Expected {expected_res}, got {res}") println(res) } catch(e){ assert(expected_res instanceof Exception, $"{name_s} expected result '{expected_res}', got Exception('{e}')}") assert(expected_res.e == null || expected_res.e==e, $"{name_s}expected Exception value = '{expected_res.e}', got '{e}'") } } try{ test(@() s.len(), 13, "s.len()") test(@() "123".tointeger(), 123, "'123'.tointeger()") test(@() "abc".tointeger(), Exception("cannot convert the string to integer"), "'abc'.tointeger()") test(@() "12.34".tofloat(), 12.34, "'12.34'.tofloat()") test(@() s.tostring(), "Hello, World!", "s.tostring()") test(@() s.hash(), s.hash(), "s.hash()") test(@() s.slice(0,5), "Hello", "s.slice(0,5)") test(@() s.indexof("World"), 7, "s.indexof('World')") test(@() s.indexof("xyz"), null, "s.indexof('xyz')") test(@() s.contains("lo"), true, "s.contains('lo')") test(@() s.contains("xyz"), false, "s.contains('xyz')") test(@() s.tolower(), "hello, world!", "s.tolower()") test(@() s.toupper(), "HELLO, WORLD!", "s.toupper()") test(@() "{Hello}, World!".subst({Hello="Hi"}), "Hi, World!", "s.subst({Hello='Hi'})") test(@() s.replace("World", "Quirrel"), "Hello, Quirrel!", "s.replace('World', 'Quirrel')") test(@() "-".join(["a","b"]), "a-b", "'-'.join(['a','b'])") test(@() " ".concat("Hello, World!", "Test"), "Hello, World! Test", "' '.concat('Hello, World!', 'Test')") test(@() s.split(",")[0], "Hello", "s.split(',')") test(@() " test ".strip(), "test", "' test '.strip()") test(@() " test".lstrip(), "test", "' test'.lstrip()") test(@() "test ".rstrip(), "test", "'test '.rstrip()") test(@() "a,b c".split_by_chars(", ")[2], "c", "'a,b c'.split_by_chars(', ')") test(@() s.startswith("Hell"), true, "s.startswith('Hell')") test(@() s.endswith("ld!"), true, "s.endswith('ld!')") test(@() s.hasindex(0), true, "s.hasindex(0)") test(@() "22a".hasindex(3), false, "'22a'.hasindex(3)") test(@() "222".hasindex(-1), false, "s.hasindex(-1)") //test(@() "\r".escape(), "\\x0'", "'\\r'.escape()") print("ok") } catch(e){ println(e) } ================================================ FILE: testData/exec/string_methods.out ================================================ 13 123 12.34 Hello, World! 4027599776755331851 Hello 7 null true false hello, world! HELLO, WORLD! Hi, World! Hello, Quirrel! a-b Hello, World! Test Hello test test test c true true true false false ok ================================================ FILE: testData/exec/sub_int_min.nut ================================================ // Test subtraction of -2147483648 (INT_MIN for 32-bit). // // The parser folds `-` into a single negative LiteralExpr. // The codegen optimizes `x - literal` to `_OP_ADDI x, -literal` when the // literal fits in 32 bits. But -(-2147483648) = +2147483648 overflows SQInt32, // wrapping to -2147483648. This causes `x - (-2147483648)` to be miscompiled // as `x + (-2147483648)` instead of `x + 2147483648`. // // Note: the unparenthesized form `x - -2147483648` is required to trigger the // bug, because the parser folds the unary minus into the literal directly. // With parens `x - (-2147483648)`, the RHS is a TO_PAREN node and the // optimization path is not taken. let x = 5 // Without parens: parser creates LiteralExpr(-2147483648) directly. // Codegen sees TO_SUB with a TO_LITERAL RHS in 32-bit range, uses _OP_ADDI. let result = x - -2147483648 // Expected: 5 - (-2147483648) = 5 + 2147483648 = 2147483653 assert(result == 2147483653) // With a variable (non-optimized path for comparison): local y = -2147483648 let result2 = x - y assert(result2 == 2147483653) // Both paths must agree. assert(result == result2) ================================================ FILE: testData/exec/sub_int_min.out ================================================ ================================================ FILE: testData/exec/surprise_js_dev.nut ================================================ println("=== Part 1: Truthy/falsy differences ===\n") // In JS: "" is falsy, 0 is falsy, [] is truthy, {} is truthy // In Quirrel... let truthy_tests = { ["empty string \"\""] = "", // JS: falsy, Quirrel: truthy ["zero 0"] = 0, // falsy (same as JS) ["zero 0.0"] = 0.0, // falsy (same as JS) ["empty array []"] = [], // JS: truthy, Quirrel: truthy too ["empty table {}"] = {}, // JS: truthy, Quirrel: truthy too } foreach (name, val in truthy_tests) { let verdict = val ? "TRUTHY" : "FALSY" println($" {name} is {verdict}") } println("\n Note: unlike JS, empty string is truthy in Quirrel.\n") println("=== Part 2: Integer division (no implicit float) ===\n") println($" JS: 10 / 3 = 3.333... | Quirrel: 10 / 3 = {10 / 3}") println($" Want float? Use 10.0 / 3 = {10.0 / 3}") println($" Negative modulo: -7 % 3 = {-7 % 3} (C-style, not math-style)\n") println("=== Part 3: Cooperative multitasking with coroutines ===\n") // Quirrel has real coroutines with bidirectional data passing. // JS generators can yield, but Quirrel threads can suspend/wakeup // with values flowing in both directions. function fibonacci_server() { local a = 0, b = 1 while (true) { local request = suspend({value = a, msg = "fib value"}) if (request == "next") { let temp = a + b; a = b; b = temp } else if (request == "reset") { a = 0; b = 1 } } } let fib = newthread(fibonacci_server) local result = fib.call() // start the coroutine print(" Fibonacci server: ") for (local i = 0; i < 10; i++) { print($"{result.value} ") result = fib.wakeup("next") // send command, receive response } fib.wakeup("reset") let after_reset = fib.wakeup("next") println($"\n After reset: {after_reset.value}") println("\n=== Part 4: Build a DSL with metamethods ===\n") // JS needs Proxy for this. Quirrel has _get, _set, _call, _add, _mul, _tostring... // Let's build a fluent SQL-like query builder using metamethods. class Query { _table = null _conditions = null _fields = null _order = null _limit_val = null constructor(table_name = null) { this._table = table_name this._conditions = [] this._fields = ["*"] this._order = null this._limit_val = null } function select(...) { let q = clone this q._fields = vargv return q } function from(table_name) { let q = clone this q._table = table_name return q } function where(condition) { let q = clone this q._conditions = clone this._conditions q._conditions.append(condition) return q } function order_by(field) { let q = clone this q._order = field return q } function limit(n) { let q = clone this q._limit_val = n return q } // Metamethod: print the query as SQL function _tostring() { let fields_str = ", ".join(this._fields) local sql = $"SELECT {fields_str} FROM {this._table}" if (this._conditions.len() > 0) { let cond_str = " AND ".join(this._conditions) sql = $"{sql} WHERE {cond_str}" } if (this._order) sql = $"{sql} ORDER BY {this._order}" if (this._limit_val != null) sql = $"{sql} LIMIT {this._limit_val}" return sql } // Metamethod: combine two queries with UNION function _add(other) { let result = this.getclass()() result._table = $"({this}) UNION ({other})" return result } } let q = Query() .select("name", "age", "email") .from("users") .where("age > 18") .where("active = true") .order_by("name") .limit(10) println($" {q}") let admins = Query().select("name").from("admins").where("role = 'super'") let mods = Query().select("name").from("moderators").where("level > 3") println($" {admins + mods}") println("\n=== Part 5: freeze - deep immutability ===\n") // JS Object.freeze is shallow and you can still reassign the variable. // Quirrel freeze is enforced at runtime - any mutation attempt throws an error. // The `static` keyword creates compile-time frozen literals. let config = freeze({ db = freeze({ host = "localhost" port = 5432 pool = freeze([5, 10, 20]) }) app = freeze({ name = "quirrel-app" version = "1.0" }) }) println($" config.app.name = {config.app.name}") println($" config is frozen: {config.is_frozen()}") println($" config.db is frozen: {config.db.is_frozen()}") println($" config.db.pool is frozen: {config.db.pool.is_frozen()}") try { config.app.name = "hacked" } catch(e) { println($" Mutation blocked: {e}") } println("\n=== Part 6: delete returns the value ===\n") #allow-delete-operator local inventory = {sword = 100, shield = 50, potion = 25} let sold_item = delete inventory.sword println($" Sold sword for {sold_item} gold! Remaining: {inventory.len()} items") // In JS, delete returns a boolean. In Quirrel, it returns the removed value. println("\n=== Part 7: The _call metamethod - objects as functions ===\n") // In JS you can't make an object callable without Proxy. // In Quirrel, _call makes any instance behave as a function. class Validator { _rules = null constructor(...) { this._rules = vargv } // env is the hidden first param - the call environment function _call(env, value) { foreach (rule in this._rules) { let result = rule(value) if (result != null) return result // return error message } return null // all passed } function _add(other) { // Combine validators with + let combined = this.getclass()() // can't use `Validator()` inside class! combined._rules = clone this._rules combined._rules.extend(other._rules) return combined } } let not_null = Validator(@(v) v == null ? "must not be null" : null) let is_string = Validator(@(v) typeof v != "string" ? "must be a string" : null) let min_len = @(n) Validator(@(v) typeof v == "string" && v.len() < n ? $"must be at least {n} chars" : null) let username_validator = not_null + is_string + min_len(3) println($" validate(null): {username_validator(null)}") println($" validate(42): {username_validator(42)}") println($" validate(\"ab\"): {username_validator("ab")}") println($" validate(\"john\"): {username_validator("john") ?? "OK"}") println("\n=== Part 8: Coroutine pipeline ===\n") // Build a lazy data processing pipeline using coroutines. // Each stage is a coroutine that pulls from the previous one. function range(start, end_val) { for (local i = start; i < end_val; i++) yield i } function map_gen(source, fn) { foreach (val in source) yield fn(val) } function filter_gen(source, pred) { foreach (val in source) if (pred(val)) yield val } function take_gen(source, n) { local count = 0 foreach (val in source) { if (count >= n) return yield val count++ } } // Pipeline: range(1,100) |> filter(odd) |> map(square) |> take(8) // All lazy - only computes what's needed. let pipeline = take_gen( map_gen( filter_gen( range(1, 100), @(x) x % 2 == 1 ), @(x) x * x ), 8 ) print(" Lazy pipeline (odd squares): ") foreach (val in pipeline) print($"{val} ") println("") println("\n=== Part 9: Three-way comparison & custom sorting ===\n") // The <=> operator returns -1, 0, or 1. JS doesn't have this. class Card { suit = null rank = null static suit_order = {clubs = 0, diamonds = 1, hearts = 2, spades = 3} constructor(rank, suit) { this.rank = rank; this.suit = suit } function _cmp(other) { let r = this.rank <=> other.rank return r != 0 ? r : this.getclass().suit_order[this.suit] <=> this.getclass().suit_order[other.suit] } function _tostring() { let symbols = {clubs = "C", diamonds = "D", hearts = "H", spades = "S"} let names = {[11] = "J", [12] = "Q", [13] = "K", [14] = "A"} let r = this.rank in names ? names[this.rank] : $"{this.rank}" return $"{r}{symbols[this.suit]}" } } local hand = [Card(14, "spades"), Card(7, "hearts"), Card(14, "clubs"), Card(7, "diamonds"), Card(10, "spades")] hand.sort(@(a, b) a <=> b) print(" Sorted hand: ") foreach (card in hand) print($"{card} ") println("\n\n=== Part 10: Destructuring + null safety chain ===\n") // Nested null-safe access - no more `obj && obj.a && obj.a.b && obj.a.b.c` let api_response = { data = { user = { profile = { avatar = "cat.png" } } } } let avatar = api_response?.data?.user?.profile?.avatar ?? "default.png" let missing = api_response?.data?.user?.settings?.theme ?? "dark" println($" Avatar: {avatar}") println($" Theme (missing path): {missing}") // Destructuring with defaults - cleaner than JS let { data = null } = api_response let { user = null } = data let { profile = {avatar = "fallback.png"} } = user println($" Destructured avatar: {profile.avatar}") ================================================ FILE: testData/exec/surprise_js_dev.out ================================================ === Part 1: Truthy/falsy differences === zero 0 is FALSY empty table {} is TRUTHY empty string "" is TRUTHY empty array [] is TRUTHY zero 0.0 is FALSY Note: unlike JS, empty string is truthy in Quirrel. === Part 2: Integer division (no implicit float) === JS: 10 / 3 = 3.333... | Quirrel: 10 / 3 = 3 Want float? Use 10.0 / 3 = 3.33333 Negative modulo: -7 % 3 = -1 (C-style, not math-style) === Part 3: Cooperative multitasking with coroutines === Fibonacci server: 0 1 1 2 3 5 8 13 21 34 After reset: 1 === Part 4: Build a DSL with metamethods === SELECT name, age, email FROM users WHERE age > 18 AND active = true ORDER BY name LIMIT 10 SELECT * FROM (SELECT name FROM admins WHERE role = 'super') UNION (SELECT name FROM moderators WHERE level > 3) === Part 5: freeze - deep immutability === config.app.name = quirrel-app config is frozen: true config.db is frozen: true config.db.pool is frozen: true Mutation blocked: trying to modify immutable 'table' === Part 6: delete returns the value === Sold sword for 100 gold! Remaining: 2 items === Part 7: The _call metamethod - objects as functions === validate(null): must not be null validate(42): must be a string validate("ab"): must be at least 3 chars validate("john"): OK === Part 8: Coroutine pipeline === Lazy pipeline (odd squares): 1 9 25 49 81 121 169 225 === Part 9: Three-way comparison & custom sorting === Sorted hand: 7D 7H 10S AC AS === Part 10: Destructuring + null safety chain === Avatar: cat.png Theme (missing path): dark Destructured avatar: cat.png ================================================ FILE: testData/exec/table_methods.nut ================================================ let t = {a=1, b=2, c=3} let Exception = class{ e = null constructor(e){ this.e=e } } function test(func, expected_res, name = null){ let name_s = name ? $"'{name}': " : "" try{ let res = func() assert(expected_res==res, @() $"{name_s}Expected {expected_res}, got {res}") println(res) } catch(e){ assert(expected_res instanceof Exception, $"{name_s} expected result '{expected_res}', got Exception('{e}')}") assert(expected_res.e == null || expected_res.e==e, $"{name_s}expected Exception value = '{expected_res.e}', got '{e}'") } } try{ test(@() t.len(), 3, "t.len()") test(@() t.rawget("a"), 1, "t.rawget('a')") test(@() t.rawget("x"), Exception("the index doesn't exist"), "t.rawget('x')") test(@() t.rawset("d", 4)["d"], 4, "t.rawset('d', 4)") test(@() t.rawdelete("d"), 4, "t.rawdelete('d')") test(@() t.rawin("b"), true, "t.rawin('b')") test(@() t.rawin("x"), false, "t.rawin('x')") test(@() t.map(@(v) v*2)["a"], 2, "t.map(@(v) v*2)") test(@() t.map(@(v, k) v*2+k.len())["a"], 3, "t.map(@(v,k) v*2 + k.len())") test(@() t.filter(@(v) v%2==1)["a"], 1, "t.filter(@(v) v%2==1)") test(@() t.reduce(@(acc,v,k) acc+v, 0), 6, "t.reduce(@(acc,k,v) acc+v, 0)") test(@() t.findindex(@(v) v==2), "b", "t.findindex(@(v) v==2)") test(@() t.findindex(@(v) v==5), null, "t.findindex(@(v) v==5)") test(@() t.findvalue(@(v) v==3), 3, "t.findvalue(@(v) v==3)") test(@() t.findvalue(@(v) v==5), null, "t.findvalue(@(v) v==5)") test(@() t.findvalue(@(_, k) k=="a"), 1, "t.findvalue(@(_,v) v==3)") test(@() t.keys().contains("a"), true, "t.keys().contains('a')") test(@() t.values().contains(2), true, "t.values().contains(2)") test(@() t.each(@(v,k) v), null, "t.each(@(k,v) v)") test(@() t.__update({b=20, d=4})["b"], 20, "t.__update({b=20, d=4})") test(@() t.__merge({e=5})["e"], 5, "t.__merge({e=5})") test(@() t.topairs().sort(@(a,b) a[0]<=>b[0])[0][0], "a", "t.topairs()[0][0]") test(@() t.replace_with({x=10, y=20})["x"], 10, "t.replace_with({x=10, y=20})") test(@() t.hasindex("x"), true, "t.hasindex('x')") test(@() t.hasindex("z"), false, "t.hasindex('z')") test(@() t.hasvalue(10), true, "t.hasvalue(10)") test(@() t.hasvalue(5), false, "t.hasvalue(5)") test(@() t.swap("x","y")["x"], 20, "t.swap('x','z')") test(@() t.clear().len(), 0, "t.clear().len()") print("ok") } catch(e){ println(e) } ================================================ FILE: testData/exec/table_methods.out ================================================ 3 1 4 4 true false 2 3 1 6 b null 3 null 1 true true null 20 5 a 10 true false true false 20 0 ok ================================================ FILE: testData/exec/testNullPropagation.nut ================================================ class A { function _get(key) { println("begin A._get() call") throw "I am an error!" println("end A._get() call") } } function testField() { let a = A() println($"a.foo = {a?.foo}") } function testIndex() { let a = A() println($"a[10] = {a?[10]}") } try { testField() println("testField failed") } catch (e) { println("testField passed") } try { testIndex() println("testIndex failed") } catch (e) { println("testIndex passed") } ================================================ FILE: testData/exec/testNullPropagation.out ================================================ begin A._get() call testField passed begin A._get() call testIndex passed ================================================ FILE: testData/exec/test_class_yield_call.nut ================================================ // Test: calling a generator method via .call() from a constructor. // // Before fix: this crashed with access violation (or assertion // "SQObjectPtr::SQObjectPtr(SQGenerator*): Assertion '_unVal.pTable' failed") // because GrowCallStack() invalidated the prevci pointer in Execute(), // causing it to enter the main instruction loop instead of returning the // generator object. The VM then hit _OP_YIELD with ci->_generator==NULL, // reaching the "only generator can be yielded" error path which itself // crashed constructing SQObjectPtr from a NULL generator pointer. // // After fix: the generator is created and returned correctly. //--- 1. Direct .call() on yielding method from constructor --- class DirectCall { constructor() { this.do_yield.call(0) } function do_yield() { yield } } DirectCall() print("1. Direct .call() from ctor: OK\n") //--- 2. Indirect .call() via helper method (original class_yield.nut) --- class IndirectCall { constructor() { this.call_yield() } function call_yield() { this.do_yield.call(0) } function do_yield() { yield } } IndirectCall() print("2. Indirect .call() from ctor: OK\n") //--- 3. .call() on yielding method with value passing --- class YieldWithValue { _result = null constructor(v) { this._result = v let g = this.gen.call(this) this._result = resume g } function gen() { yield this._result + 100 } } let obj3 = YieldWithValue(5) assert(obj3._result == 105) print("3. .call() with value passing: OK\n") //--- 4. Inherited yielding method called via .call() from derived ctor --- class Base4 { function do_yield() { yield 42 } } class Derived4(Base4) { constructor() { this.do_yield.call(0) } } Derived4() print("4. Inherited yield via .call(): OK\n") //--- 5. Deep nesting that triggers multiple callstack reallocations --- // Initial callstack size is 4; each recursion adds a frame. // The generator .call() from the constructor must survive GrowCallStack(). function deep(n) { if (n <= 0) { class Inner { constructor() { this.gen.call(0) } function gen() { yield } } Inner() return 1 } return deep(n - 1) + 1 } assert(deep(30) == 31) print("5. Deep nesting + yield .call(): OK\n") //--- 6. Generator method .call() outside constructor (control case) --- class Normal6 { _v = 0 constructor(v) { this._v = v } function gen() { yield this._v; yield this._v + 1 } } let obj6 = Normal6(10) let g6 = obj6.gen.call(obj6) assert(resume g6 == 10) assert(resume g6 == 11) print("6. Generator .call() outside ctor: OK\n") //--- 7. Multiple generators created via .call() in constructor --- class Multi7 { _g1 = null _g2 = null constructor() { this._g1 = this.gen1.call(this) this._g2 = this.gen2.call(this) } function gen1() { yield 10; yield 20 } function gen2() { yield 30; yield 40 } } let obj7 = Multi7() assert(resume obj7._g1 == 10) assert(resume obj7._g2 == 30) assert(resume obj7._g1 == 20) assert(resume obj7._g2 == 40) print("7. Multiple generators from ctor: OK\n") //--- 8. newthread + generator function --- let gen8 = function() { yield 21 } let th8 = newthread(gen8) let r8 = th8.call() assert(type(r8) == "generator") assert(resume r8 == 21) print("8. newthread + generator: OK\n") print("\nAll tests passed.\n") ================================================ FILE: testData/exec/test_class_yield_call.out ================================================ 1. Direct .call() from ctor: OK 2. Indirect .call() from ctor: OK 3. .call() with value passing: OK 4. Inherited yield via .call(): OK 5. Deep nesting + yield .call(): OK 6. Generator .call() outside ctor: OK 7. Multiple generators from ctor: OK 8. newthread + generator: OK All tests passed. ================================================ FILE: testData/exec/test_shift.nut ================================================ from "string" import * local INT_MAX = (-1) >>> 1 local INT_MIN = -INT_MAX - 1 local BITS = format("%x", INT_MAX).len() * 4 local MAXBIT = BITS - 1 // 63 for 64-bit, 31 for 32-bit println(format("INT_MAX = %d, INT_MIN = %d, BITS = %d\n", INT_MAX, INT_MIN, BITS)) local total = 0 local passed = 0 local failed = 0 function check_eq(got, expected, desc) { total++ if (got == expected) { passed++ println(format(" OK %-62s == %20d", desc, got)) } else { failed++ println(format(" FAIL %-62s == %20d (expected %d)", desc, got, expected)) } } // ================================================================ // SECTION 1: Tests with LOCAL VARIABLES // ================================================================ println("==============================================================") println(" SECTION 1: Shifts with local variables") println("==============================================================\n") // --- << (left shift) with local variables --- println("\n[local <<] basic") { local x, y x = 1; y = 0; check_eq(x << y, 1, "x=1 << y=0") x = 1; y = 1; check_eq(x << y, 2, "x=1 << y=1") x = 1; y = 2; check_eq(x << y, 4, "x=1 << y=2") x = 1; y = 10; check_eq(x << y, 1024, "x=1 << y=10") x = 3; y = 4; check_eq(x << y, 48, "x=3 << y=4") x = 0xFF; y = 8; check_eq(x << y, 65280, "x=0xFF << y=8") } println("\n[local <<] zero value") { local x = 0, y y = 0; check_eq(x << y, 0, "x=0 << y=0") y = 1; check_eq(x << y, 0, "x=0 << y=1") y = MAXBIT; check_eq(x << y, 0, format("x=0 << y=%d", MAXBIT)) y = -1; check_eq(x << y, 0, "x=0 << y=-1") } println("\n[local <<] negative shift (direction reversal)") { local x, y x = 8; y = -1; check_eq(x << y, 4, "x=8 << y=-1") x = 8; y = -2; check_eq(x << y, 2, "x=8 << y=-2") x = 8; y = -3; check_eq(x << y, 1, "x=8 << y=-3") x = 1024; y = -10; check_eq(x << y, 1, "x=1024 << y=-10") x = 16; y = -1; check_eq(x << y, 8, "x=16 << y=-1") } println(format("\n[local <<] overflow (shift >= %d)", BITS)) { local x, y x = 1; y = BITS; check_eq(x << y, 0, format("x=1 << y=%d", BITS)) x = 1; y = BITS + 1; check_eq(x << y, 0, format("x=1 << y=%d", BITS + 1)) x = 1; y = BITS * 3; check_eq(x << y, 0, format("x=1 << y=%d", BITS * 3)) x = -1; y = BITS; check_eq(x << y, 0, format("x=-1 << y=%d", BITS)) x = INT_MAX; y = BITS; check_eq(x << y, 0, format("x=INT_MAX << y=%d", BITS)) } println("\n[local <<] negative value") { local x = -1, y y = 0; check_eq(x << y, -1, "x=-1 << y=0") y = 1; check_eq(x << y, -2, "x=-1 << y=1") y = MAXBIT; check_eq(x << y, INT_MIN, format("x=-1 << y=%d", MAXBIT)) } println("\n[local <<] extreme values") { local x, y x = INT_MAX; y = 0; check_eq(x << y, INT_MAX, "x=INT_MAX << y=0") x = INT_MAX; y = 1; check_eq(x << y, -2, "x=INT_MAX << y=1") x = INT_MAX; y = BITS; check_eq(x << y, 0, format("x=INT_MAX << y=%d", BITS)) x = INT_MIN; y = 0; check_eq(x << y, INT_MIN, "x=INT_MIN << y=0") x = INT_MIN; y = 1; check_eq(x << y, 0, "x=INT_MIN << y=1") x = INT_MIN; y = BITS; check_eq(x << y, 0, format("x=INT_MIN << y=%d", BITS)) x = 1; y = INT_MIN; check_eq(x << y, 0, "x=1 << y=INT_MIN") x = -1; y = INT_MIN; check_eq(x << y, -1, "x=-1 << y=INT_MIN") x = 1; y = INT_MAX; check_eq(x << y, 0, "x=1 << y=INT_MAX") x = -1; y = INT_MAX; check_eq(x << y, 0, "x=-1 << y=INT_MAX") x = INT_MAX; y = -1; check_eq(x << y, INT_MAX / 2, "x=INT_MAX << y=-1") x = INT_MIN; y = -1; check_eq(x << y, INT_MIN / 2, "x=INT_MIN << y=-1") // arithmetic >> 1 of 0x800...0 x = 1; y = -BITS; check_eq(x << y, 0, format("x=1 << y=-%d", BITS)) x = 1; y = -(BITS + 1); check_eq(x << y, 0, format("x=1 << y=-%d", BITS + 1)) x = 1; y = -(BITS * 3); check_eq(x << y, 0, format("x=1 << y=-%d", BITS * 3)) } println("\n[local <<] boundary 31/32/33 and 63/64/65") { local x, y // positive shifts x = 1; y = 31; check_eq(x << y, 2147483648, "x=1 << y=31") x = 1; y = 32; check_eq(x << y, 4294967296, "x=1 << y=32") x = 1; y = 33; check_eq(x << y, 8589934592, "x=1 << y=33") x = 1; y = 63; check_eq(x << y, INT_MIN, "x=1 << y=63") x = 1; y = 64; check_eq(x << y, 0, "x=1 << y=64") x = 1; y = 65; check_eq(x << y, 0, "x=1 << y=65") x = -1; y = 31; check_eq(x << y, -2147483648, "x=-1 << y=31") x = -1; y = 32; check_eq(x << y, -4294967296, "x=-1 << y=32") x = -1; y = 33; check_eq(x << y, -8589934592, "x=-1 << y=33") x = -1; y = 63; check_eq(x << y, INT_MIN, "x=-1 << y=63") x = -1; y = 64; check_eq(x << y, 0, "x=-1 << y=64") x = -1; y = 65; check_eq(x << y, 0, "x=-1 << y=65") // negative shifts (reverse to >>) x = INT_MIN; y = -31; check_eq(x << y, -4294967296, "x=INT_MIN << y=-31") // arithmetic >> 31 x = INT_MIN; y = -32; check_eq(x << y, -2147483648, "x=INT_MIN << y=-32") x = INT_MIN; y = -33; check_eq(x << y, -1073741824, "x=INT_MIN << y=-33") x = INT_MIN; y = -63; check_eq(x << y, -1, "x=INT_MIN << y=-63") x = INT_MIN; y = -64; check_eq(x << y, -1, "x=INT_MIN << y=-64") x = INT_MIN; y = -65; check_eq(x << y, -1, "x=INT_MIN << y=-65") } // --- >> (arithmetic right shift) with local variables --- println("\n[local >>] basic") { local x, y x = 8; y = 0; check_eq(x >> y, 8, "x=8 >> y=0") x = 8; y = 1; check_eq(x >> y, 4, "x=8 >> y=1") x = 8; y = 2; check_eq(x >> y, 2, "x=8 >> y=2") x = 8; y = 3; check_eq(x >> y, 1, "x=8 >> y=3") x = 8; y = 4; check_eq(x >> y, 0, "x=8 >> y=4") x = 1024; y = 10; check_eq(x >> y, 1, "x=1024 >> y=10") x = 0xFF00; y = 8; check_eq(x >> y, 255, "x=0xFF00 >> y=8") } println("\n[local >>] zero value") { local x = 0, y y = 0; check_eq(x >> y, 0, "x=0 >> y=0") y = 1; check_eq(x >> y, 0, "x=0 >> y=1") y = MAXBIT; check_eq(x >> y, 0, format("x=0 >> y=%d", MAXBIT)) y = -1; check_eq(x >> y, 0, "x=0 >> y=-1") } println("\n[local >>] negative shift (direction reversal)") { local x, y x = 1; y = -1; check_eq(x >> y, 2, "x=1 >> y=-1") x = 1; y = -2; check_eq(x >> y, 4, "x=1 >> y=-2") x = 1; y = -10; check_eq(x >> y, 1024, "x=1 >> y=-10") x = 3; y = -4; check_eq(x >> y, 48, "x=3 >> y=-4") } println("\n[local >>] overflow positive") { local x, y x = 1; y = BITS; check_eq(x >> y, 0, format("x=1 >> y=%d", BITS)) x = 1; y = BITS + 1; check_eq(x >> y, 0, format("x=1 >> y=%d", BITS + 1)) x = 1; y = BITS * 3; check_eq(x >> y, 0, format("x=1 >> y=%d", BITS * 3)) x = INT_MAX; y = BITS; check_eq(x >> y, 0, format("x=INT_MAX >> y=%d", BITS)) } println("\n[local >>] overflow negative (sign fill)") { local x, y x = -1; y = BITS; check_eq(x >> y, -1, format("x=-1 >> y=%d", BITS)) x = -1; y = BITS + 1; check_eq(x >> y, -1, format("x=-1 >> y=%d", BITS + 1)) x = -1; y = BITS * 3; check_eq(x >> y, -1, format("x=-1 >> y=%d", BITS * 3)) x = INT_MIN; y = BITS; check_eq(x >> y, -1, format("x=INT_MIN >> y=%d", BITS)) x = -42; y = BITS; check_eq(x >> y, -1, format("x=-42 >> y=%d", BITS)) } println("\n[local >>] negative value") { local x, y x = -1; y = 1; check_eq(x >> y, -1, "x=-1 >> y=1") x = -2; y = 1; check_eq(x >> y, -1, "x=-2 >> y=1") x = INT_MIN; y = 1; check_eq(x >> y, INT_MIN / 2, "x=INT_MIN >> y=1") } println("\n[local >>] extreme values") { local x, y x = INT_MAX; y = 0; check_eq(x >> y, INT_MAX, "x=INT_MAX >> y=0") x = INT_MIN; y = 0; check_eq(x >> y, INT_MIN, "x=INT_MIN >> y=0") x = 1; y = INT_MIN; check_eq(x >> y, 0, "x=1 >> y=INT_MIN") x = -1; y = INT_MIN; check_eq(x >> y, -1, "x=-1 >> y=INT_MIN") x = INT_MIN; y = INT_MIN; check_eq(x >> y, -1, "x=INT_MIN >> y=INT_MIN") x = 1; y = INT_MAX; check_eq(x >> y, 0, "x=1 >> y=INT_MAX") x = -1; y = INT_MAX; check_eq(x >> y, -1, "x=-1 >> y=INT_MAX") x = 1; y = -1; check_eq(x >> y, 2, "x=1 >> y=-1") x = INT_MAX; y = -1; check_eq(x >> y, -2, "x=INT_MAX >> y=-1") x = 1; y = -BITS; check_eq(x >> y, 0, format("x=1 >> y=-%d", BITS)) x = -1; y = -BITS; check_eq(x >> y, -1, format("x=-1 >> y=-%d", BITS)) x = 1; y = -(BITS*3);check_eq(x >> y, 0, format("x=1 >> y=-%d", BITS * 3)) } println("\n[local >>] boundary 31/32/33 and 63/64/65") { local x, y x = 1; y = 31; check_eq(x >> y, 0, "x=1 >> y=31") x = 1; y = 32; check_eq(x >> y, 0, "x=1 >> y=32") x = 1; y = 33; check_eq(x >> y, 0, "x=1 >> y=33") x = 1; y = 63; check_eq(x >> y, 0, "x=1 >> y=63") x = 1; y = 64; check_eq(x >> y, 0, "x=1 >> y=64") x = 1; y = 65; check_eq(x >> y, 0, "x=1 >> y=65") x = -1; y = 31; check_eq(x >> y, -1, "x=-1 >> y=31") x = -1; y = 32; check_eq(x >> y, -1, "x=-1 >> y=32") x = -1; y = 33; check_eq(x >> y, -1, "x=-1 >> y=33") x = -1; y = 63; check_eq(x >> y, -1, "x=-1 >> y=63") x = -1; y = 64; check_eq(x >> y, -1, "x=-1 >> y=64") x = -1; y = 65; check_eq(x >> y, -1, "x=-1 >> y=65") x = INT_MIN; y = 31; check_eq(x >> y, -4294967296, "x=INT_MIN >> y=31") x = INT_MIN; y = 32; check_eq(x >> y, -2147483648, "x=INT_MIN >> y=32") x = INT_MIN; y = 33; check_eq(x >> y, -1073741824, "x=INT_MIN >> y=33") x = INT_MIN; y = 63; check_eq(x >> y, -1, "x=INT_MIN >> y=63") x = INT_MIN; y = 64; check_eq(x >> y, -1, "x=INT_MIN >> y=64") x = INT_MIN; y = 65; check_eq(x >> y, -1, "x=INT_MIN >> y=65") // negative shifts (reverse to <<) x = 1; y = -31; check_eq(x >> y, 2147483648, "x=1 >> y=-31") x = 1; y = -32; check_eq(x >> y, 4294967296, "x=1 >> y=-32") x = 1; y = -33; check_eq(x >> y, 8589934592, "x=1 >> y=-33") x = 1; y = -63; check_eq(x >> y, INT_MIN, "x=1 >> y=-63") x = 1; y = -64; check_eq(x >> y, 0, "x=1 >> y=-64") x = 1; y = -65; check_eq(x >> y, 0, "x=1 >> y=-65") x = INT_MIN; y = -31; check_eq(x >> y, 0, "x=INT_MIN >> y=-31") x = INT_MIN; y = -32; check_eq(x >> y, 0, "x=INT_MIN >> y=-32") x = INT_MIN; y = -33; check_eq(x >> y, 0, "x=INT_MIN >> y=-33") x = INT_MIN; y = -63; check_eq(x >> y, 0, "x=INT_MIN >> y=-63") x = INT_MIN; y = -64; check_eq(x >> y, -1, "x=INT_MIN >> y=-64") x = INT_MIN; y = -65; check_eq(x >> y, -1, "x=INT_MIN >> y=-65") } // --- >>> (unsigned right shift) with local variables --- println("\n[local >>>] basic") { local x, y x = 8; y = 0; check_eq(x >>> y, 8, "x=8 >>> y=0") x = 8; y = 1; check_eq(x >>> y, 4, "x=8 >>> y=1") x = 8; y = 2; check_eq(x >>> y, 2, "x=8 >>> y=2") x = 8; y = 3; check_eq(x >>> y, 1, "x=8 >>> y=3") x = 8; y = 4; check_eq(x >>> y, 0, "x=8 >>> y=4") x = 0xFF00; y = 8; check_eq(x >>> y, 255, "x=0xFF00 >>> y=8") } println("\n[local >>>] zero value") { local x = 0, y y = 0; check_eq(x >>> y, 0, "x=0 >>> y=0") y = 1; check_eq(x >>> y, 0, "x=0 >>> y=1") y = MAXBIT; check_eq(x >>> y, 0, format("x=0 >>> y=%d", MAXBIT)) y = -1; check_eq(x >>> y, 0, "x=0 >>> y=-1") } println("\n[local >>>] negative shift (direction reversal)") { local x, y x = 1; y = -1; check_eq(x >>> y, 2, "x=1 >>> y=-1") x = 1; y = -2; check_eq(x >>> y, 4, "x=1 >>> y=-2") x = 1; y = -10; check_eq(x >>> y, 1024, "x=1 >>> y=-10") } println(format("\n[local >>>] overflow (shift >= %d)", BITS)) { local x, y x = 1; y = BITS; check_eq(x >>> y, 0, format("x=1 >>> y=%d", BITS)) x = 1; y = BITS + 1; check_eq(x >>> y, 0, format("x=1 >>> y=%d", BITS + 1)) x = -1; y = BITS; check_eq(x >>> y, 0, format("x=-1 >>> y=%d", BITS)) x = INT_MIN; y = BITS; check_eq(x >>> y, 0, format("x=INT_MIN >>> y=%d", BITS)) x = INT_MAX; y = BITS; check_eq(x >>> y, 0, format("x=INT_MAX >>> y=%d", BITS)) } println("\n[local >>>] negative value") { local x, y x = -1; y = 1; check_eq(x >>> y, INT_MAX, "x=-1 >>> y=1") x = INT_MIN; y = 1; check_eq(x >>> y, 1 << (MAXBIT - 1), "x=INT_MIN >>> y=1") x = -1; y = MAXBIT; check_eq(x >>> y, 1, format("x=-1 >>> y=%d", MAXBIT)) } println("\n[local >>>] extreme values") { local x, y x = INT_MAX; y = 0; check_eq(x >>> y, INT_MAX, "x=INT_MAX >>> y=0") x = INT_MIN; y = 0; check_eq(x >>> y, INT_MIN, "x=INT_MIN >>> y=0") x = 1; y = INT_MIN; check_eq(x >>> y, 0, "x=1 >>> y=INT_MIN") x = -1; y = INT_MIN; check_eq(x >>> y, 0, "x=-1 >>> y=INT_MIN") x = INT_MIN; y = INT_MIN; check_eq(x >>> y, 0, "x=INT_MIN >>> y=INT_MIN") x = 1; y = INT_MAX; check_eq(x >>> y, 0, "x=1 >>> y=INT_MAX") x = -1; y = INT_MAX; check_eq(x >>> y, 0, "x=-1 >>> y=INT_MAX") x = 1; y = -1; check_eq(x >>> y, 2, "x=1 >>> y=-1") x = INT_MAX; y = -1; check_eq(x >>> y, -2, "x=INT_MAX >>> y=-1") x = 1; y = -BITS; check_eq(x >>> y, 0, format("x=1 >>> y=-%d", BITS)) x = -1; y = -BITS; check_eq(x >>> y, 0, format("x=-1 >>> y=-%d", BITS)) x = 1; y = -(BITS*3); check_eq(x >>> y, 0, format("x=1 >>> y=-%d", BITS * 3)) } println("\n[local >>>] boundary 31/32/33 and 63/64/65") { local x, y x = 1; y = 31; check_eq(x >>> y, 0, "x=1 >>> y=31") x = 1; y = 32; check_eq(x >>> y, 0, "x=1 >>> y=32") x = 1; y = 33; check_eq(x >>> y, 0, "x=1 >>> y=33") x = 1; y = 63; check_eq(x >>> y, 0, "x=1 >>> y=63") x = 1; y = 64; check_eq(x >>> y, 0, "x=1 >>> y=64") x = 1; y = 65; check_eq(x >>> y, 0, "x=1 >>> y=65") x = -1; y = 31; check_eq(x >>> y, 8589934591, "x=-1 >>> y=31") x = -1; y = 32; check_eq(x >>> y, 4294967295, "x=-1 >>> y=32") x = -1; y = 33; check_eq(x >>> y, 2147483647, "x=-1 >>> y=33") x = -1; y = 63; check_eq(x >>> y, 1, "x=-1 >>> y=63") x = -1; y = 64; check_eq(x >>> y, 0, "x=-1 >>> y=64") x = -1; y = 65; check_eq(x >>> y, 0, "x=-1 >>> y=65") x = INT_MIN; y = 31; check_eq(x >>> y, 4294967296, "x=INT_MIN >>> y=31") x = INT_MIN; y = 32; check_eq(x >>> y, 2147483648, "x=INT_MIN >>> y=32") x = INT_MIN; y = 33; check_eq(x >>> y, 1073741824, "x=INT_MIN >>> y=33") x = INT_MIN; y = 63; check_eq(x >>> y, 1, "x=INT_MIN >>> y=63") x = INT_MIN; y = 64; check_eq(x >>> y, 0, "x=INT_MIN >>> y=64") x = INT_MIN; y = 65; check_eq(x >>> y, 0, "x=INT_MIN >>> y=65") // negative shifts (reverse to <<) x = 1; y = -31; check_eq(x >>> y, 2147483648, "x=1 >>> y=-31") x = 1; y = -32; check_eq(x >>> y, 4294967296, "x=1 >>> y=-32") x = 1; y = -33; check_eq(x >>> y, 8589934592, "x=1 >>> y=-33") x = 1; y = -63; check_eq(x >>> y, INT_MIN, "x=1 >>> y=-63") x = 1; y = -64; check_eq(x >>> y, 0, "x=1 >>> y=-64") x = 1; y = -65; check_eq(x >>> y, 0, "x=1 >>> y=-65") x = INT_MIN; y = -31; check_eq(x >>> y, 0, "x=INT_MIN >>> y=-31") x = INT_MIN; y = -32; check_eq(x >>> y, 0, "x=INT_MIN >>> y=-32") x = INT_MIN; y = -33; check_eq(x >>> y, 0, "x=INT_MIN >>> y=-33") x = INT_MIN; y = -63; check_eq(x >>> y, 0, "x=INT_MIN >>> y=-63") x = INT_MIN; y = -64; check_eq(x >>> y, 0, "x=INT_MIN >>> y=-64") x = INT_MIN; y = -65; check_eq(x >>> y, 0, "x=INT_MIN >>> y=-65") } // --- cross-function with local variables --- println("\n[local cross] left/right inverse") { local x, y x = 1; y = 10; check_eq((x << y) >> y, 1, "(x=1 << y=10) >> y=10") x = 42; y = 5; check_eq((x << y) >> y, 42, "(x=42 << y=5) >> y=5") } println(format("\n[local cross] shift by %d (boundary)", MAXBIT)) { local x, y x = 1; y = MAXBIT; check_eq(x << y, INT_MIN, format("x=1 << y=%d", MAXBIT)) x = INT_MIN; y = MAXBIT; check_eq(x >> y, -1, format("x=INT_MIN >> y=%d", MAXBIT)) x = INT_MIN; y = MAXBIT; check_eq(x >>> y, 1, format("x=INT_MIN >>> y=%d", MAXBIT)) x = -1; y = MAXBIT; check_eq(x >> y, -1, format("x=-1 >> y=%d", MAXBIT)) x = -1; y = MAXBIT; check_eq(x >>> y, 1, format("x=-1 >>> y=%d", MAXBIT)) } // ================================================================ // SECTION 2: Tests with NUMERIC CONSTANTS (literal expressions) // ================================================================ println("\n==============================================================") println(" SECTION 2: Shifts with numeric constants") println("==============================================================\n") // --- << with constants --- println("\n[const expr <<] basic") check_eq(1 << 0, 1, "1 << 0") check_eq(1 << 1, 2, "1 << 1") check_eq(1 << 2, 4, "1 << 2") check_eq(1 << 10, 1024, "1 << 10") check_eq(3 << 4, 48, "3 << 4") check_eq(0xFF << 8, 65280, "0xFF << 8") println("\n[const expr <<] zero value") check_eq(0 << 0, 0, "0 << 0") check_eq(0 << 1, 0, "0 << 1") check_eq(0 << -1, 0, "0 << -1") println("\n[const expr <<] negative shift") check_eq(8 << -1, 4, "8 << -1") check_eq(8 << -2, 2, "8 << -2") check_eq(8 << -3, 1, "8 << -3") check_eq(1024 << -10, 1, "1024 << -10") check_eq(16 << -1, 8, "16 << -1") println("\n[const expr <<] negative value") check_eq(-1 << 0, -1, "-1 << 0") check_eq(-1 << 1, -2, "-1 << 1") println("\n[const expr <<] 32-bit range") // These are valid (non-overflow) shifts in 64-bit check_eq(1 << 31, 2147483648, "1 << 31") check_eq(1 << 32, 4294967296, "1 << 32") check_eq(-1 << 31, -2147483648, "-1 << 31") check_eq(-1 << 32, -4294967296, "-1 << 32") println("\n[const expr <<] overflow (shift >= BITS)") { local x x = 1; check_eq(x << BITS, 0, format("1 << %d", BITS)) x = -1; check_eq(x << BITS, 0, format("-1 << %d", BITS)) x = 1; check_eq(x << (BITS + 1), 0, format("1 << %d", BITS + 1)) } println("\n[const expr <<] extreme") check_eq(1 << -32, 0, "1 << -32") check_eq(1 << -33, 0, "1 << -33") check_eq(1 << -100, 0, "1 << -100") { local x x = INT_MAX; check_eq(x << 0, INT_MAX, "INT_MAX << 0") x = INT_MAX; check_eq(x << 1, -2, "INT_MAX << 1") x = INT_MIN; check_eq(x << 0, INT_MIN, "INT_MIN << 0") x = INT_MIN; check_eq(x << 1, 0, "INT_MIN << 1") } println("\n[const expr <<] boundary 31/32/33 and 63/64/65") check_eq(1 << 33, 8589934592, "1 << 33") check_eq(1 << 63, INT_MIN, "1 << 63") check_eq(1 << 64, 0, "1 << 64") check_eq(1 << 65, 0, "1 << 65") check_eq(-1 << 33, -8589934592, "-1 << 33") check_eq(-1 << 63, INT_MIN, "-1 << 63") check_eq(-1 << 64, 0, "-1 << 64") check_eq(-1 << 65, 0, "-1 << 65") { local x x = INT_MIN; check_eq(x << -31, -4294967296, "INT_MIN << -31") x = INT_MIN; check_eq(x << -32, -2147483648, "INT_MIN << -32") x = INT_MIN; check_eq(x << -33, -1073741824, "INT_MIN << -33") x = INT_MIN; check_eq(x << -63, -1, "INT_MIN << -63") x = INT_MIN; check_eq(x << -64, -1, "INT_MIN << -64") x = INT_MIN; check_eq(x << -65, -1, "INT_MIN << -65") } // --- >> with constants --- println("\n[const expr >>] basic") check_eq(8 >> 0, 8, "8 >> 0") check_eq(8 >> 1, 4, "8 >> 1") check_eq(8 >> 2, 2, "8 >> 2") check_eq(8 >> 3, 1, "8 >> 3") check_eq(8 >> 4, 0, "8 >> 4") check_eq(1024 >> 10, 1, "1024 >> 10") check_eq(0xFF00 >> 8, 255, "0xFF00 >> 8") println("\n[const expr >>] zero value") check_eq(0 >> 0, 0, "0 >> 0") check_eq(0 >> 1, 0, "0 >> 1") check_eq(0 >> -1, 0, "0 >> -1") println("\n[const expr >>] negative shift") check_eq(1 >> -1, 2, "1 >> -1") check_eq(1 >> -2, 4, "1 >> -2") check_eq(1 >> -10, 1024, "1 >> -10") check_eq(3 >> -4, 48, "3 >> -4") println("\n[const expr >>] overflow") { local x x = 1; check_eq(x >> BITS, 0, format("1 >> %d", BITS)) x = 1; check_eq(x >> (BITS + 1), 0, format("1 >> %d", BITS + 1)) x = INT_MAX; check_eq(x >> BITS, 0, format("INT_MAX >> %d", BITS)) x = -1; check_eq(x >> BITS, -1, format("-1 >> %d", BITS)) x = -1; check_eq(x >> (BITS + 1),-1, format("-1 >> %d", BITS + 1)) x = INT_MIN; check_eq(x >> BITS, -1, format("INT_MIN >> %d", BITS)) x = -42; check_eq(x >> BITS, -1, format("-42 >> %d", BITS)) } println("\n[const expr >>] negative value") { local x x = -1; check_eq(x >> 1, -1, "-1 >> 1") x = -2; check_eq(x >> 1, -1, "-2 >> 1") x = INT_MIN; check_eq(x >> 1, INT_MIN / 2, "INT_MIN >> 1") } println("\n[const expr >>] extreme") check_eq(1 >> -32, 4294967296, "1 >> -32") // reverses to 1 << 32, valid in 64-bit check_eq(1 >> -100, 0, "1 >> -100") // reverses to 1 << 100, overflow { local x x = INT_MAX; check_eq(x >> 0, INT_MAX, "INT_MAX >> 0") x = INT_MIN; check_eq(x >> 0, INT_MIN, "INT_MIN >> 0") } println("\n[const expr >>] boundary 31/32/33 and 63/64/65") { local x // x=-1 positive shifts x = -1; check_eq(x >> 31, -1, "-1 >> 31") x = -1; check_eq(x >> 32, -1, "-1 >> 32") x = -1; check_eq(x >> 33, -1, "-1 >> 33") x = -1; check_eq(x >> 63, -1, "-1 >> 63") x = -1; check_eq(x >> 64, -1, "-1 >> 64") x = -1; check_eq(x >> 65, -1, "-1 >> 65") x = INT_MIN; check_eq(x >> 31, -4294967296, "INT_MIN >> 31") x = INT_MIN; check_eq(x >> 32, -2147483648, "INT_MIN >> 32") x = INT_MIN; check_eq(x >> 33, -1073741824, "INT_MIN >> 33") x = INT_MIN; check_eq(x >> 63, -1, "INT_MIN >> 63") x = INT_MIN; check_eq(x >> 64, -1, "INT_MIN >> 64") x = INT_MIN; check_eq(x >> 65, -1, "INT_MIN >> 65") // negative shifts (reverse to <<) x = 1; check_eq(x >> -31, 2147483648, "1 >> -31") x = 1; check_eq(x >> -33, 8589934592, "1 >> -33") x = 1; check_eq(x >> -63, INT_MIN, "1 >> -63") x = 1; check_eq(x >> -64, 0, "1 >> -64") x = 1; check_eq(x >> -65, 0, "1 >> -65") x = INT_MIN; check_eq(x >> -31, 0, "INT_MIN >> -31") x = INT_MIN; check_eq(x >> -32, 0, "INT_MIN >> -32") x = INT_MIN; check_eq(x >> -33, 0, "INT_MIN >> -33") x = INT_MIN; check_eq(x >> -63, 0, "INT_MIN >> -63") x = INT_MIN; check_eq(x >> -64, -1, "INT_MIN >> -64") x = INT_MIN; check_eq(x >> -65, -1, "INT_MIN >> -65") } // --- >>> with constants --- println("\n[const expr >>>] basic") check_eq(8 >>> 0, 8, "8 >>> 0") check_eq(8 >>> 1, 4, "8 >>> 1") check_eq(8 >>> 2, 2, "8 >>> 2") check_eq(8 >>> 3, 1, "8 >>> 3") check_eq(8 >>> 4, 0, "8 >>> 4") check_eq(0xFF00 >>> 8, 255, "0xFF00 >>> 8") println("\n[const expr >>>] zero value") check_eq(0 >>> 0, 0, "0 >>> 0") check_eq(0 >>> 1, 0, "0 >>> 1") check_eq(0 >>> -1, 0, "0 >>> -1") println("\n[const expr >>>] negative shift") check_eq(1 >>> -1, 2, "1 >>> -1") check_eq(1 >>> -2, 4, "1 >>> -2") check_eq(1 >>> -10, 1024, "1 >>> -10") println("\n[const expr >>>] overflow") { local x x = 1; check_eq(x >>> BITS, 0, format("1 >>> %d", BITS)) x = 1; check_eq(x >>> (BITS + 1), 0, format("1 >>> %d", BITS + 1)) x = -1; check_eq(x >>> BITS, 0, format("-1 >>> %d", BITS)) x = INT_MIN; check_eq(x >>> BITS, 0, format("INT_MIN >>> %d", BITS)) x = INT_MAX; check_eq(x >>> BITS, 0, format("INT_MAX >>> %d", BITS)) } println("\n[const expr >>>] negative value") { local x x = -1; check_eq(x >>> 1, INT_MAX, "-1 >>> 1") x = INT_MIN; check_eq(x >>> 1, 1 << (MAXBIT - 1), "INT_MIN >>> 1") x = -1; check_eq(x >>> MAXBIT, 1, format("-1 >>> %d", MAXBIT)) } println("\n[const expr >>>] extreme") check_eq(1 >>> -32, 4294967296, "1 >>> -32") // reverses to 1 << 32, valid in 64-bit check_eq(1 >>> -100, 0, "1 >>> -100") // reverses to 1 << 100, overflow { local x x = INT_MAX; check_eq(x >>> 0, INT_MAX, "INT_MAX >>> 0") x = INT_MIN; check_eq(x >>> 0, INT_MIN, "INT_MIN >>> 0") } println("\n[const expr >>>] boundary 31/32/33 and 63/64/65") { local x x = -1; check_eq(x >>> 31, 8589934591, "-1 >>> 31") x = -1; check_eq(x >>> 32, 4294967295, "-1 >>> 32") x = -1; check_eq(x >>> 33, 2147483647, "-1 >>> 33") x = -1; check_eq(x >>> 63, 1, "-1 >>> 63") x = -1; check_eq(x >>> 64, 0, "-1 >>> 64") x = -1; check_eq(x >>> 65, 0, "-1 >>> 65") x = INT_MIN; check_eq(x >>> 31, 4294967296, "INT_MIN >>> 31") x = INT_MIN; check_eq(x >>> 32, 2147483648, "INT_MIN >>> 32") x = INT_MIN; check_eq(x >>> 33, 1073741824, "INT_MIN >>> 33") x = INT_MIN; check_eq(x >>> 63, 1, "INT_MIN >>> 63") x = INT_MIN; check_eq(x >>> 64, 0, "INT_MIN >>> 64") x = INT_MIN; check_eq(x >>> 65, 0, "INT_MIN >>> 65") // negative shifts (reverse to <<) x = 1; check_eq(x >>> -31, 2147483648, "1 >>> -31") x = 1; check_eq(x >>> -33, 8589934592, "1 >>> -33") x = 1; check_eq(x >>> -63, INT_MIN, "1 >>> -63") x = 1; check_eq(x >>> -64, 0, "1 >>> -64") x = 1; check_eq(x >>> -65, 0, "1 >>> -65") x = INT_MIN; check_eq(x >>> -31, 0, "INT_MIN >>> -31") x = INT_MIN; check_eq(x >>> -32, 0, "INT_MIN >>> -32") x = INT_MIN; check_eq(x >>> -33, 0, "INT_MIN >>> -33") x = INT_MIN; check_eq(x >>> -63, 0, "INT_MIN >>> -63") x = INT_MIN; check_eq(x >>> -64, 0, "INT_MIN >>> -64") x = INT_MIN; check_eq(x >>> -65, 0, "INT_MIN >>> -65") } // --- cross with constants --- println(format("\n[const expr cross] shift by %d", MAXBIT)) { local x x = 1; check_eq(x << MAXBIT, INT_MIN, format("1 << %d", MAXBIT)) x = INT_MIN; check_eq(x >> MAXBIT, -1, format("INT_MIN >> %d", MAXBIT)) x = INT_MIN; check_eq(x >>> MAXBIT, 1, format("INT_MIN >>> %d", MAXBIT)) x = -1; check_eq(x >> MAXBIT, -1, format("-1 >> %d", MAXBIT)) x = -1; check_eq(x >>> MAXBIT, 1, format("-1 >>> %d", MAXBIT)) } // ================================================================ // SECTION 3: Compile-time CONST assignments (const X = literal op literal) // ================================================================ println("\n==============================================================") println(" SECTION 3: Shifts assigned to const (compile-time folding)") println("==============================================================\n") // --- << assigned to const --- println("\n[const <<] basic") const SHL_1_0 = 1 << 0 const SHL_1_1 = 1 << 1 const SHL_1_2 = 1 << 2 const SHL_1_10 = 1 << 10 const SHL_3_4 = 3 << 4 const SHL_FF_8 = 0xFF << 8 check_eq(SHL_1_0, 1, "const = 1 << 0") check_eq(SHL_1_1, 2, "const = 1 << 1") check_eq(SHL_1_2, 4, "const = 1 << 2") check_eq(SHL_1_10, 1024, "const = 1 << 10") check_eq(SHL_3_4, 48, "const = 3 << 4") check_eq(SHL_FF_8, 65280, "const = 0xFF << 8") println("\n[const <<] zero value") const SHL_0_0 = 0 << 0 const SHL_0_1 = 0 << 1 const SHL_0_N1 = 0 << -1 check_eq(SHL_0_0, 0, "const = 0 << 0") check_eq(SHL_0_1, 0, "const = 0 << 1") check_eq(SHL_0_N1, 0, "const = 0 << -1") println("\n[const <<] negative shift") const SHL_8_N1 = 8 << -1 const SHL_8_N2 = 8 << -2 const SHL_8_N3 = 8 << -3 const SHL_1024_N10 = 1024 << -10 const SHL_16_N1 = 16 << -1 check_eq(SHL_8_N1, 4, "const = 8 << -1") check_eq(SHL_8_N2, 2, "const = 8 << -2") check_eq(SHL_8_N3, 1, "const = 8 << -3") check_eq(SHL_1024_N10, 1, "const = 1024 << -10") check_eq(SHL_16_N1, 8, "const = 16 << -1") println("\n[const <<] negative value") const SHL_N1_0 = -1 << 0 const SHL_N1_1 = -1 << 1 check_eq(SHL_N1_0, -1, "const = -1 << 0") check_eq(SHL_N1_1, -2, "const = -1 << 1") println("\n[const <<] 32-bit range (valid in 64-bit)") const SHL_1_31 = 1 << 31 const SHL_1_32 = 1 << 32 const SHL_N1_31 = -1 << 31 const SHL_N1_32 = -1 << 32 check_eq(SHL_1_31, 2147483648, "const = 1 << 31") check_eq(SHL_1_32, 4294967296, "const = 1 << 32") check_eq(SHL_N1_31, -2147483648, "const = -1 << 31") check_eq(SHL_N1_32, -4294967296, "const = -1 << 32") println("\n[const <<] overflow") const SHL_1_N32 = 1 << -32 const SHL_1_N33 = 1 << -33 const SHL_1_N100 = 1 << -100 check_eq(SHL_1_N32, 0, "const = 1 << -32") check_eq(SHL_1_N33, 0, "const = 1 << -33") check_eq(SHL_1_N100, 0, "const = 1 << -100") println("\n[const <<] boundary 33/63/64/65") const SHL_1_33 = 1 << 33 const SHL_1_63 = 1 << 63 const SHL_1_64 = 1 << 64 const SHL_1_65 = 1 << 65 const SHL_N1_33 = -1 << 33 const SHL_N1_63 = -1 << 63 const SHL_N1_64 = -1 << 64 const SHL_N1_65 = -1 << 65 check_eq(SHL_1_33, 8589934592, "const = 1 << 33") check_eq(SHL_1_63, INT_MIN, "const = 1 << 63") check_eq(SHL_1_64, 0, "const = 1 << 64") check_eq(SHL_1_65, 0, "const = 1 << 65") check_eq(SHL_N1_33, -8589934592, "const = -1 << 33") check_eq(SHL_N1_63, INT_MIN, "const = -1 << 63") check_eq(SHL_N1_64, 0, "const = -1 << 64") check_eq(SHL_N1_65, 0, "const = -1 << 65") const SHL_1_N31 = 1 << -31 const SHL_1_N63 = 1 << -63 const SHL_1_N64 = 1 << -64 const SHL_1_N65 = 1 << -65 check_eq(SHL_1_N31, 0, "const = 1 << -31") check_eq(SHL_1_N63, 0, "const = 1 << -63") check_eq(SHL_1_N64, 0, "const = 1 << -64") check_eq(SHL_1_N65, 0, "const = 1 << -65") // --- >> assigned to const --- println("\n[const >>] basic") const SHR_8_0 = 8 >> 0 const SHR_8_1 = 8 >> 1 const SHR_8_2 = 8 >> 2 const SHR_8_3 = 8 >> 3 const SHR_8_4 = 8 >> 4 const SHR_1024_10 = 1024 >> 10 const SHR_FF00_8 = 0xFF00 >> 8 check_eq(SHR_8_0, 8, "const = 8 >> 0") check_eq(SHR_8_1, 4, "const = 8 >> 1") check_eq(SHR_8_2, 2, "const = 8 >> 2") check_eq(SHR_8_3, 1, "const = 8 >> 3") check_eq(SHR_8_4, 0, "const = 8 >> 4") check_eq(SHR_1024_10, 1, "const = 1024 >> 10") check_eq(SHR_FF00_8, 255, "const = 0xFF00 >> 8") println("\n[const >>] zero value") const SHR_0_0 = 0 >> 0 const SHR_0_1 = 0 >> 1 const SHR_0_N1 = 0 >> -1 check_eq(SHR_0_0, 0, "const = 0 >> 0") check_eq(SHR_0_1, 0, "const = 0 >> 1") check_eq(SHR_0_N1, 0, "const = 0 >> -1") println("\n[const >>] negative shift") const SHR_1_N1 = 1 >> -1 const SHR_1_N2 = 1 >> -2 const SHR_1_N10 = 1 >> -10 const SHR_3_N4 = 3 >> -4 check_eq(SHR_1_N1, 2, "const = 1 >> -1") check_eq(SHR_1_N2, 4, "const = 1 >> -2") check_eq(SHR_1_N10, 1024, "const = 1 >> -10") check_eq(SHR_3_N4, 48, "const = 3 >> -4") println("\n[const >>] negative shift (large)") const SHR_1_N32 = 1 >> -32 // reverses to 1 << 32, valid in 64-bit const SHR_1_N100 = 1 >> -100 // reverses to 1 << 100, overflow in 64-bit check_eq(SHR_1_N32, 4294967296, "const = 1 >> -32") check_eq(SHR_1_N100, 0, "const = 1 >> -100") println("\n[const >>] 32-bit range (valid in 64-bit)") const SHR_BIG_31 = 4294967296 >> 31 // 2^32 >> 31 = 2 const SHR_BIG_32 = 4294967296 >> 32 // 2^32 >> 32 = 1 check_eq(SHR_BIG_31, 2, "const = 4294967296 >> 31") check_eq(SHR_BIG_32, 1, "const = 4294967296 >> 32") println("\n[const >>] boundary 33/63/64/65") const SHR_BIG_33 = 4294967296 >> 33 // 2^32 >> 33 = 0 (shifted past) const SHR_BIG2_31 = 8589934592 >> 31 // 2^33 >> 31 = 4 const SHR_BIG2_32 = 8589934592 >> 32 // 2^33 >> 32 = 2 const SHR_BIG2_33 = 8589934592 >> 33 // 2^33 >> 33 = 1 check_eq(SHR_BIG_33, 0, "const = 4294967296 >> 33") check_eq(SHR_BIG2_31, 4, "const = 8589934592 >> 31") check_eq(SHR_BIG2_32, 2, "const = 8589934592 >> 32") check_eq(SHR_BIG2_33, 1, "const = 8589934592 >> 33") const SHR_N1_31 = -1 >> 31 const SHR_N1_32 = -1 >> 32 const SHR_N1_33 = -1 >> 33 const SHR_N1_63 = -1 >> 63 const SHR_N1_64 = -1 >> 64 const SHR_N1_65 = -1 >> 65 check_eq(SHR_N1_31, -1, "const = -1 >> 31") check_eq(SHR_N1_32, -1, "const = -1 >> 32") check_eq(SHR_N1_33, -1, "const = -1 >> 33") check_eq(SHR_N1_63, -1, "const = -1 >> 63") check_eq(SHR_N1_64, -1, "const = -1 >> 64") check_eq(SHR_N1_65, -1, "const = -1 >> 65") const SHR_1_N31 = 1 >> -31 const SHR_1_N33 = 1 >> -33 const SHR_1_N63 = 1 >> -63 const SHR_1_N64 = 1 >> -64 const SHR_1_N65 = 1 >> -65 check_eq(SHR_1_N31, 2147483648, "const = 1 >> -31") check_eq(SHR_1_N33, 8589934592, "const = 1 >> -33") check_eq(SHR_1_N63, INT_MIN, "const = 1 >> -63") check_eq(SHR_1_N64, 0, "const = 1 >> -64") check_eq(SHR_1_N65, 0, "const = 1 >> -65") // --- >>> assigned to const --- println("\n[const >>>] basic") const USHR_8_0 = 8 >>> 0 const USHR_8_1 = 8 >>> 1 const USHR_8_2 = 8 >>> 2 const USHR_8_3 = 8 >>> 3 const USHR_8_4 = 8 >>> 4 const USHR_FF00_8 = 0xFF00 >>> 8 check_eq(USHR_8_0, 8, "const = 8 >>> 0") check_eq(USHR_8_1, 4, "const = 8 >>> 1") check_eq(USHR_8_2, 2, "const = 8 >>> 2") check_eq(USHR_8_3, 1, "const = 8 >>> 3") check_eq(USHR_8_4, 0, "const = 8 >>> 4") check_eq(USHR_FF00_8, 255, "const = 0xFF00 >>> 8") println("\n[const >>>] zero value") const USHR_0_0 = 0 >>> 0 const USHR_0_1 = 0 >>> 1 const USHR_0_N1 = 0 >>> -1 check_eq(USHR_0_0, 0, "const = 0 >>> 0") check_eq(USHR_0_1, 0, "const = 0 >>> 1") check_eq(USHR_0_N1, 0, "const = 0 >>> -1") println("\n[const >>>] negative shift") const USHR_1_N1 = 1 >>> -1 const USHR_1_N2 = 1 >>> -2 const USHR_1_N10 = 1 >>> -10 check_eq(USHR_1_N1, 2, "const = 1 >>> -1") check_eq(USHR_1_N2, 4, "const = 1 >>> -2") check_eq(USHR_1_N10, 1024, "const = 1 >>> -10") println("\n[const >>>] negative shift (large)") const USHR_1_N32 = 1 >>> -32 // reverses to 1 << 32, valid in 64-bit const USHR_1_N100 = 1 >>> -100 // reverses to 1 << 100, overflow in 64-bit check_eq(USHR_1_N32, 4294967296, "const = 1 >>> -32") check_eq(USHR_1_N100, 0, "const = 1 >>> -100") println("\n[const >>>] 32-bit range (valid in 64-bit)") const USHR_BIG_31 = 4294967296 >>> 31 // 2^32 >>> 31 = 2 const USHR_BIG_32 = 4294967296 >>> 32 // 2^32 >>> 32 = 1 const USHR_N1_1 = -1 >>> 1 check_eq(USHR_BIG_31, 2, "const = 4294967296 >>> 31") check_eq(USHR_BIG_32, 1, "const = 4294967296 >>> 32") check_eq(USHR_N1_1, INT_MAX, "const = -1 >>> 1") println("\n[const >>>] boundary 33/63/64/65") const USHR_BIG_33 = 4294967296 >>> 33 // 2^32 >>> 33 = 0 const USHR_BIG2_31 = 8589934592 >>> 31 // 2^33 >>> 31 = 4 const USHR_BIG2_32 = 8589934592 >>> 32 // 2^33 >>> 32 = 2 const USHR_BIG2_33 = 8589934592 >>> 33 // 2^33 >>> 33 = 1 check_eq(USHR_BIG_33, 0, "const = 4294967296 >>> 33") check_eq(USHR_BIG2_31, 4, "const = 8589934592 >>> 31") check_eq(USHR_BIG2_32, 2, "const = 8589934592 >>> 32") check_eq(USHR_BIG2_33, 1, "const = 8589934592 >>> 33") const USHR_N1_31 = -1 >>> 31 const USHR_N1_32 = -1 >>> 32 const USHR_N1_33 = -1 >>> 33 const USHR_N1_63 = -1 >>> 63 const USHR_N1_64 = -1 >>> 64 const USHR_N1_65 = -1 >>> 65 check_eq(USHR_N1_31, 8589934591, "const = -1 >>> 31") check_eq(USHR_N1_32, 4294967295, "const = -1 >>> 32") check_eq(USHR_N1_33, 2147483647, "const = -1 >>> 33") check_eq(USHR_N1_63, 1, "const = -1 >>> 63") check_eq(USHR_N1_64, 0, "const = -1 >>> 64") check_eq(USHR_N1_65, 0, "const = -1 >>> 65") const USHR_1_N31 = 1 >>> -31 const USHR_1_N33 = 1 >>> -33 const USHR_1_N63 = 1 >>> -63 const USHR_1_N64 = 1 >>> -64 const USHR_1_N65 = 1 >>> -65 check_eq(USHR_1_N31, 2147483648, "const = 1 >>> -31") check_eq(USHR_1_N33, 8589934592, "const = 1 >>> -33") check_eq(USHR_1_N63, INT_MIN, "const = 1 >>> -63") check_eq(USHR_1_N64, 0, "const = 1 >>> -64") check_eq(USHR_1_N65, 0, "const = 1 >>> -65") // --- const cross --- println("\n[const cross] basic identities") const CROSS_INV_A = (1 << 10) >> 10 const CROSS_INV_B = (42 << 5) >> 5 check_eq(CROSS_INV_A, 1, "const = (1 << 10) >> 10") check_eq(CROSS_INV_B, 42, "const = (42 << 5) >> 5") // ================================================================ // Summary // ================================================================ println(format("\n=== Results: %d/%d passed", passed, total)) if (failed > 0) print(format(", %d FAILED", failed)) println(" ===") if (failed > 0) throw format("%d test(s) failed", failed) ================================================ FILE: testData/exec/test_shift.out ================================================ INT_MAX = 9223372036854775807, INT_MIN = -9223372036854775808, BITS = 64 ============================================================== SECTION 1: Shifts with local variables ============================================================== [local <<] basic OK x=1 << y=0 == 1 OK x=1 << y=1 == 2 OK x=1 << y=2 == 4 OK x=1 << y=10 == 1024 OK x=3 << y=4 == 48 OK x=0xFF << y=8 == 65280 [local <<] zero value OK x=0 << y=0 == 0 OK x=0 << y=1 == 0 OK x=0 << y=63 == 0 OK x=0 << y=-1 == 0 [local <<] negative shift (direction reversal) OK x=8 << y=-1 == 4 OK x=8 << y=-2 == 2 OK x=8 << y=-3 == 1 OK x=1024 << y=-10 == 1 OK x=16 << y=-1 == 8 [local <<] overflow (shift >= 64) OK x=1 << y=64 == 0 OK x=1 << y=65 == 0 OK x=1 << y=192 == 0 OK x=-1 << y=64 == 0 OK x=INT_MAX << y=64 == 0 [local <<] negative value OK x=-1 << y=0 == -1 OK x=-1 << y=1 == -2 OK x=-1 << y=63 == -9223372036854775808 [local <<] extreme values OK x=INT_MAX << y=0 == 9223372036854775807 OK x=INT_MAX << y=1 == -2 OK x=INT_MAX << y=64 == 0 OK x=INT_MIN << y=0 == -9223372036854775808 OK x=INT_MIN << y=1 == 0 OK x=INT_MIN << y=64 == 0 OK x=1 << y=INT_MIN == 0 OK x=-1 << y=INT_MIN == -1 OK x=1 << y=INT_MAX == 0 OK x=-1 << y=INT_MAX == 0 OK x=INT_MAX << y=-1 == 4611686018427387903 OK x=INT_MIN << y=-1 == -4611686018427387904 OK x=1 << y=-64 == 0 OK x=1 << y=-65 == 0 OK x=1 << y=-192 == 0 [local <<] boundary 31/32/33 and 63/64/65 OK x=1 << y=31 == 2147483648 OK x=1 << y=32 == 4294967296 OK x=1 << y=33 == 8589934592 OK x=1 << y=63 == -9223372036854775808 OK x=1 << y=64 == 0 OK x=1 << y=65 == 0 OK x=-1 << y=31 == -2147483648 OK x=-1 << y=32 == -4294967296 OK x=-1 << y=33 == -8589934592 OK x=-1 << y=63 == -9223372036854775808 OK x=-1 << y=64 == 0 OK x=-1 << y=65 == 0 OK x=INT_MIN << y=-31 == -4294967296 OK x=INT_MIN << y=-32 == -2147483648 OK x=INT_MIN << y=-33 == -1073741824 OK x=INT_MIN << y=-63 == -1 OK x=INT_MIN << y=-64 == -1 OK x=INT_MIN << y=-65 == -1 [local >>] basic OK x=8 >> y=0 == 8 OK x=8 >> y=1 == 4 OK x=8 >> y=2 == 2 OK x=8 >> y=3 == 1 OK x=8 >> y=4 == 0 OK x=1024 >> y=10 == 1 OK x=0xFF00 >> y=8 == 255 [local >>] zero value OK x=0 >> y=0 == 0 OK x=0 >> y=1 == 0 OK x=0 >> y=63 == 0 OK x=0 >> y=-1 == 0 [local >>] negative shift (direction reversal) OK x=1 >> y=-1 == 2 OK x=1 >> y=-2 == 4 OK x=1 >> y=-10 == 1024 OK x=3 >> y=-4 == 48 [local >>] overflow positive OK x=1 >> y=64 == 0 OK x=1 >> y=65 == 0 OK x=1 >> y=192 == 0 OK x=INT_MAX >> y=64 == 0 [local >>] overflow negative (sign fill) OK x=-1 >> y=64 == -1 OK x=-1 >> y=65 == -1 OK x=-1 >> y=192 == -1 OK x=INT_MIN >> y=64 == -1 OK x=-42 >> y=64 == -1 [local >>] negative value OK x=-1 >> y=1 == -1 OK x=-2 >> y=1 == -1 OK x=INT_MIN >> y=1 == -4611686018427387904 [local >>] extreme values OK x=INT_MAX >> y=0 == 9223372036854775807 OK x=INT_MIN >> y=0 == -9223372036854775808 OK x=1 >> y=INT_MIN == 0 OK x=-1 >> y=INT_MIN == -1 OK x=INT_MIN >> y=INT_MIN == -1 OK x=1 >> y=INT_MAX == 0 OK x=-1 >> y=INT_MAX == -1 OK x=1 >> y=-1 == 2 OK x=INT_MAX >> y=-1 == -2 OK x=1 >> y=-64 == 0 OK x=-1 >> y=-64 == -1 OK x=1 >> y=-192 == 0 [local >>] boundary 31/32/33 and 63/64/65 OK x=1 >> y=31 == 0 OK x=1 >> y=32 == 0 OK x=1 >> y=33 == 0 OK x=1 >> y=63 == 0 OK x=1 >> y=64 == 0 OK x=1 >> y=65 == 0 OK x=-1 >> y=31 == -1 OK x=-1 >> y=32 == -1 OK x=-1 >> y=33 == -1 OK x=-1 >> y=63 == -1 OK x=-1 >> y=64 == -1 OK x=-1 >> y=65 == -1 OK x=INT_MIN >> y=31 == -4294967296 OK x=INT_MIN >> y=32 == -2147483648 OK x=INT_MIN >> y=33 == -1073741824 OK x=INT_MIN >> y=63 == -1 OK x=INT_MIN >> y=64 == -1 OK x=INT_MIN >> y=65 == -1 OK x=1 >> y=-31 == 2147483648 OK x=1 >> y=-32 == 4294967296 OK x=1 >> y=-33 == 8589934592 OK x=1 >> y=-63 == -9223372036854775808 OK x=1 >> y=-64 == 0 OK x=1 >> y=-65 == 0 OK x=INT_MIN >> y=-31 == 0 OK x=INT_MIN >> y=-32 == 0 OK x=INT_MIN >> y=-33 == 0 OK x=INT_MIN >> y=-63 == 0 OK x=INT_MIN >> y=-64 == -1 OK x=INT_MIN >> y=-65 == -1 [local >>>] basic OK x=8 >>> y=0 == 8 OK x=8 >>> y=1 == 4 OK x=8 >>> y=2 == 2 OK x=8 >>> y=3 == 1 OK x=8 >>> y=4 == 0 OK x=0xFF00 >>> y=8 == 255 [local >>>] zero value OK x=0 >>> y=0 == 0 OK x=0 >>> y=1 == 0 OK x=0 >>> y=63 == 0 OK x=0 >>> y=-1 == 0 [local >>>] negative shift (direction reversal) OK x=1 >>> y=-1 == 2 OK x=1 >>> y=-2 == 4 OK x=1 >>> y=-10 == 1024 [local >>>] overflow (shift >= 64) OK x=1 >>> y=64 == 0 OK x=1 >>> y=65 == 0 OK x=-1 >>> y=64 == 0 OK x=INT_MIN >>> y=64 == 0 OK x=INT_MAX >>> y=64 == 0 [local >>>] negative value OK x=-1 >>> y=1 == 9223372036854775807 OK x=INT_MIN >>> y=1 == 4611686018427387904 OK x=-1 >>> y=63 == 1 [local >>>] extreme values OK x=INT_MAX >>> y=0 == 9223372036854775807 OK x=INT_MIN >>> y=0 == -9223372036854775808 OK x=1 >>> y=INT_MIN == 0 OK x=-1 >>> y=INT_MIN == 0 OK x=INT_MIN >>> y=INT_MIN == 0 OK x=1 >>> y=INT_MAX == 0 OK x=-1 >>> y=INT_MAX == 0 OK x=1 >>> y=-1 == 2 OK x=INT_MAX >>> y=-1 == -2 OK x=1 >>> y=-64 == 0 OK x=-1 >>> y=-64 == 0 OK x=1 >>> y=-192 == 0 [local >>>] boundary 31/32/33 and 63/64/65 OK x=1 >>> y=31 == 0 OK x=1 >>> y=32 == 0 OK x=1 >>> y=33 == 0 OK x=1 >>> y=63 == 0 OK x=1 >>> y=64 == 0 OK x=1 >>> y=65 == 0 OK x=-1 >>> y=31 == 8589934591 OK x=-1 >>> y=32 == 4294967295 OK x=-1 >>> y=33 == 2147483647 OK x=-1 >>> y=63 == 1 OK x=-1 >>> y=64 == 0 OK x=-1 >>> y=65 == 0 OK x=INT_MIN >>> y=31 == 4294967296 OK x=INT_MIN >>> y=32 == 2147483648 OK x=INT_MIN >>> y=33 == 1073741824 OK x=INT_MIN >>> y=63 == 1 OK x=INT_MIN >>> y=64 == 0 OK x=INT_MIN >>> y=65 == 0 OK x=1 >>> y=-31 == 2147483648 OK x=1 >>> y=-32 == 4294967296 OK x=1 >>> y=-33 == 8589934592 OK x=1 >>> y=-63 == -9223372036854775808 OK x=1 >>> y=-64 == 0 OK x=1 >>> y=-65 == 0 OK x=INT_MIN >>> y=-31 == 0 OK x=INT_MIN >>> y=-32 == 0 OK x=INT_MIN >>> y=-33 == 0 OK x=INT_MIN >>> y=-63 == 0 OK x=INT_MIN >>> y=-64 == 0 OK x=INT_MIN >>> y=-65 == 0 [local cross] left/right inverse OK (x=1 << y=10) >> y=10 == 1 OK (x=42 << y=5) >> y=5 == 42 [local cross] shift by 63 (boundary) OK x=1 << y=63 == -9223372036854775808 OK x=INT_MIN >> y=63 == -1 OK x=INT_MIN >>> y=63 == 1 OK x=-1 >> y=63 == -1 OK x=-1 >>> y=63 == 1 ============================================================== SECTION 2: Shifts with numeric constants ============================================================== [const expr <<] basic OK 1 << 0 == 1 OK 1 << 1 == 2 OK 1 << 2 == 4 OK 1 << 10 == 1024 OK 3 << 4 == 48 OK 0xFF << 8 == 65280 [const expr <<] zero value OK 0 << 0 == 0 OK 0 << 1 == 0 OK 0 << -1 == 0 [const expr <<] negative shift OK 8 << -1 == 4 OK 8 << -2 == 2 OK 8 << -3 == 1 OK 1024 << -10 == 1 OK 16 << -1 == 8 [const expr <<] negative value OK -1 << 0 == -1 OK -1 << 1 == -2 [const expr <<] 32-bit range OK 1 << 31 == 2147483648 OK 1 << 32 == 4294967296 OK -1 << 31 == -2147483648 OK -1 << 32 == -4294967296 [const expr <<] overflow (shift >= BITS) OK 1 << 64 == 0 OK -1 << 64 == 0 OK 1 << 65 == 0 [const expr <<] extreme OK 1 << -32 == 0 OK 1 << -33 == 0 OK 1 << -100 == 0 OK INT_MAX << 0 == 9223372036854775807 OK INT_MAX << 1 == -2 OK INT_MIN << 0 == -9223372036854775808 OK INT_MIN << 1 == 0 [const expr <<] boundary 31/32/33 and 63/64/65 OK 1 << 33 == 8589934592 OK 1 << 63 == -9223372036854775808 OK 1 << 64 == 0 OK 1 << 65 == 0 OK -1 << 33 == -8589934592 OK -1 << 63 == -9223372036854775808 OK -1 << 64 == 0 OK -1 << 65 == 0 OK INT_MIN << -31 == -4294967296 OK INT_MIN << -32 == -2147483648 OK INT_MIN << -33 == -1073741824 OK INT_MIN << -63 == -1 OK INT_MIN << -64 == -1 OK INT_MIN << -65 == -1 [const expr >>] basic OK 8 >> 0 == 8 OK 8 >> 1 == 4 OK 8 >> 2 == 2 OK 8 >> 3 == 1 OK 8 >> 4 == 0 OK 1024 >> 10 == 1 OK 0xFF00 >> 8 == 255 [const expr >>] zero value OK 0 >> 0 == 0 OK 0 >> 1 == 0 OK 0 >> -1 == 0 [const expr >>] negative shift OK 1 >> -1 == 2 OK 1 >> -2 == 4 OK 1 >> -10 == 1024 OK 3 >> -4 == 48 [const expr >>] overflow OK 1 >> 64 == 0 OK 1 >> 65 == 0 OK INT_MAX >> 64 == 0 OK -1 >> 64 == -1 OK -1 >> 65 == -1 OK INT_MIN >> 64 == -1 OK -42 >> 64 == -1 [const expr >>] negative value OK -1 >> 1 == -1 OK -2 >> 1 == -1 OK INT_MIN >> 1 == -4611686018427387904 [const expr >>] extreme OK 1 >> -32 == 4294967296 OK 1 >> -100 == 0 OK INT_MAX >> 0 == 9223372036854775807 OK INT_MIN >> 0 == -9223372036854775808 [const expr >>] boundary 31/32/33 and 63/64/65 OK -1 >> 31 == -1 OK -1 >> 32 == -1 OK -1 >> 33 == -1 OK -1 >> 63 == -1 OK -1 >> 64 == -1 OK -1 >> 65 == -1 OK INT_MIN >> 31 == -4294967296 OK INT_MIN >> 32 == -2147483648 OK INT_MIN >> 33 == -1073741824 OK INT_MIN >> 63 == -1 OK INT_MIN >> 64 == -1 OK INT_MIN >> 65 == -1 OK 1 >> -31 == 2147483648 OK 1 >> -33 == 8589934592 OK 1 >> -63 == -9223372036854775808 OK 1 >> -64 == 0 OK 1 >> -65 == 0 OK INT_MIN >> -31 == 0 OK INT_MIN >> -32 == 0 OK INT_MIN >> -33 == 0 OK INT_MIN >> -63 == 0 OK INT_MIN >> -64 == -1 OK INT_MIN >> -65 == -1 [const expr >>>] basic OK 8 >>> 0 == 8 OK 8 >>> 1 == 4 OK 8 >>> 2 == 2 OK 8 >>> 3 == 1 OK 8 >>> 4 == 0 OK 0xFF00 >>> 8 == 255 [const expr >>>] zero value OK 0 >>> 0 == 0 OK 0 >>> 1 == 0 OK 0 >>> -1 == 0 [const expr >>>] negative shift OK 1 >>> -1 == 2 OK 1 >>> -2 == 4 OK 1 >>> -10 == 1024 [const expr >>>] overflow OK 1 >>> 64 == 0 OK 1 >>> 65 == 0 OK -1 >>> 64 == 0 OK INT_MIN >>> 64 == 0 OK INT_MAX >>> 64 == 0 [const expr >>>] negative value OK -1 >>> 1 == 9223372036854775807 OK INT_MIN >>> 1 == 4611686018427387904 OK -1 >>> 63 == 1 [const expr >>>] extreme OK 1 >>> -32 == 4294967296 OK 1 >>> -100 == 0 OK INT_MAX >>> 0 == 9223372036854775807 OK INT_MIN >>> 0 == -9223372036854775808 [const expr >>>] boundary 31/32/33 and 63/64/65 OK -1 >>> 31 == 8589934591 OK -1 >>> 32 == 4294967295 OK -1 >>> 33 == 2147483647 OK -1 >>> 63 == 1 OK -1 >>> 64 == 0 OK -1 >>> 65 == 0 OK INT_MIN >>> 31 == 4294967296 OK INT_MIN >>> 32 == 2147483648 OK INT_MIN >>> 33 == 1073741824 OK INT_MIN >>> 63 == 1 OK INT_MIN >>> 64 == 0 OK INT_MIN >>> 65 == 0 OK 1 >>> -31 == 2147483648 OK 1 >>> -33 == 8589934592 OK 1 >>> -63 == -9223372036854775808 OK 1 >>> -64 == 0 OK 1 >>> -65 == 0 OK INT_MIN >>> -31 == 0 OK INT_MIN >>> -32 == 0 OK INT_MIN >>> -33 == 0 OK INT_MIN >>> -63 == 0 OK INT_MIN >>> -64 == 0 OK INT_MIN >>> -65 == 0 [const expr cross] shift by 63 OK 1 << 63 == -9223372036854775808 OK INT_MIN >> 63 == -1 OK INT_MIN >>> 63 == 1 OK -1 >> 63 == -1 OK -1 >>> 63 == 1 ============================================================== SECTION 3: Shifts assigned to const (compile-time folding) ============================================================== [const <<] basic OK const = 1 << 0 == 1 OK const = 1 << 1 == 2 OK const = 1 << 2 == 4 OK const = 1 << 10 == 1024 OK const = 3 << 4 == 48 OK const = 0xFF << 8 == 65280 [const <<] zero value OK const = 0 << 0 == 0 OK const = 0 << 1 == 0 OK const = 0 << -1 == 0 [const <<] negative shift OK const = 8 << -1 == 4 OK const = 8 << -2 == 2 OK const = 8 << -3 == 1 OK const = 1024 << -10 == 1 OK const = 16 << -1 == 8 [const <<] negative value OK const = -1 << 0 == -1 OK const = -1 << 1 == -2 [const <<] 32-bit range (valid in 64-bit) OK const = 1 << 31 == 2147483648 OK const = 1 << 32 == 4294967296 OK const = -1 << 31 == -2147483648 OK const = -1 << 32 == -4294967296 [const <<] overflow OK const = 1 << -32 == 0 OK const = 1 << -33 == 0 OK const = 1 << -100 == 0 [const <<] boundary 33/63/64/65 OK const = 1 << 33 == 8589934592 OK const = 1 << 63 == -9223372036854775808 OK const = 1 << 64 == 0 OK const = 1 << 65 == 0 OK const = -1 << 33 == -8589934592 OK const = -1 << 63 == -9223372036854775808 OK const = -1 << 64 == 0 OK const = -1 << 65 == 0 OK const = 1 << -31 == 0 OK const = 1 << -63 == 0 OK const = 1 << -64 == 0 OK const = 1 << -65 == 0 [const >>] basic OK const = 8 >> 0 == 8 OK const = 8 >> 1 == 4 OK const = 8 >> 2 == 2 OK const = 8 >> 3 == 1 OK const = 8 >> 4 == 0 OK const = 1024 >> 10 == 1 OK const = 0xFF00 >> 8 == 255 [const >>] zero value OK const = 0 >> 0 == 0 OK const = 0 >> 1 == 0 OK const = 0 >> -1 == 0 [const >>] negative shift OK const = 1 >> -1 == 2 OK const = 1 >> -2 == 4 OK const = 1 >> -10 == 1024 OK const = 3 >> -4 == 48 [const >>] negative shift (large) OK const = 1 >> -32 == 4294967296 OK const = 1 >> -100 == 0 [const >>] 32-bit range (valid in 64-bit) OK const = 4294967296 >> 31 == 2 OK const = 4294967296 >> 32 == 1 [const >>] boundary 33/63/64/65 OK const = 4294967296 >> 33 == 0 OK const = 8589934592 >> 31 == 4 OK const = 8589934592 >> 32 == 2 OK const = 8589934592 >> 33 == 1 OK const = -1 >> 31 == -1 OK const = -1 >> 32 == -1 OK const = -1 >> 33 == -1 OK const = -1 >> 63 == -1 OK const = -1 >> 64 == -1 OK const = -1 >> 65 == -1 OK const = 1 >> -31 == 2147483648 OK const = 1 >> -33 == 8589934592 OK const = 1 >> -63 == -9223372036854775808 OK const = 1 >> -64 == 0 OK const = 1 >> -65 == 0 [const >>>] basic OK const = 8 >>> 0 == 8 OK const = 8 >>> 1 == 4 OK const = 8 >>> 2 == 2 OK const = 8 >>> 3 == 1 OK const = 8 >>> 4 == 0 OK const = 0xFF00 >>> 8 == 255 [const >>>] zero value OK const = 0 >>> 0 == 0 OK const = 0 >>> 1 == 0 OK const = 0 >>> -1 == 0 [const >>>] negative shift OK const = 1 >>> -1 == 2 OK const = 1 >>> -2 == 4 OK const = 1 >>> -10 == 1024 [const >>>] negative shift (large) OK const = 1 >>> -32 == 4294967296 OK const = 1 >>> -100 == 0 [const >>>] 32-bit range (valid in 64-bit) OK const = 4294967296 >>> 31 == 2 OK const = 4294967296 >>> 32 == 1 OK const = -1 >>> 1 == 9223372036854775807 [const >>>] boundary 33/63/64/65 OK const = 4294967296 >>> 33 == 0 OK const = 8589934592 >>> 31 == 4 OK const = 8589934592 >>> 32 == 2 OK const = 8589934592 >>> 33 == 1 OK const = -1 >>> 31 == 8589934591 OK const = -1 >>> 32 == 4294967295 OK const = -1 >>> 33 == 2147483647 OK const = -1 >>> 63 == 1 OK const = -1 >>> 64 == 0 OK const = -1 >>> 65 == 0 OK const = 1 >>> -31 == 2147483648 OK const = 1 >>> -33 == 8589934592 OK const = 1 >>> -63 == -9223372036854775808 OK const = 1 >>> -64 == 0 OK const = 1 >>> -65 == 0 [const cross] basic identities OK const = (1 << 10) >> 10 == 1 OK const = (42 << 5) >> 5 == 42 === Results: 444/444 passed === ================================================ FILE: testData/exec/test_stale_stkbase.nut ================================================ // Test: stack reallocation during native constructor leaves stale _stkbase // Bug: In _OP_CALL for OT_CLASS with native constructor (OT_NATIVECLOSURE), // CallNative -> EnterFrame may resize the stack, but there is no RELOAD_STK // after CallNative returns. Subsequent instructions use stale _stkbase pointer. // // Location: sqvm.cpp, _OP_CALL, case OT_CLASS, case OT_NATIVECLOSURE (~line 1110-1116) let {regexp} = require("string") // Recursive function that creates regexp instances at each depth. // At some depth, the regexp constructor's CallNative->EnterFrame triggers // a stack reallocation. The missing RELOAD_STK means the next instruction // after _OP_CALL uses a stale _stkbase (use-after-free). function test(n) { if (n <= 0) return 0 // _OP_CALL for regexp(".") goes through: // OT_CLASS -> CreateClassInstance -> CallNative(ctor) -> EnterFrame -> resize! // break (NO RELOAD_STK!) // Next instruction reads from stale _stkbase local r = regexp(".") local v = test(n - 1) return v } test(500) print("done\n") ================================================ FILE: testData/exec/test_stale_stkbase.out ================================================ done ================================================ FILE: testData/exec/tostring_recursion.nut ================================================ // Demonstrates SQVM::ToString -> CallMetaMethod -> Call recursion (1.6) // // ToString() checks for MT_TOSTRING metamethod on instance's class. // If found, it calls CallMetaMethod() -> Call() -> Execute(). // If the _tostring body does string concatenation on 'this', // StringCat calls ToString(this) again -> infinite recursion. // // Call chain: // "" + obj // -> StringCat(str, obj) // -> ToString(obj) // -> GetMetaMethod(MT_TOSTRING) -> found _tostring // -> CallMetaMethod(_tostring, 1, res) // -> Call(_tostring closure) // -> Execute() // -> "obj" + this (inside _tostring body) // -> StringCat("obj", this) // -> ToString(this) <-- re-enters! // -> CallMetaMethod again -> ... local depth = 0 class Recursive { function _tostring() { depth++ // This concatenation calls StringCat -> ToString(this) -> _tostring again return "obj" + this } } local obj = Recursive() try { let s = "" + obj print(s) } catch(e) { println($"Caught after depth={depth}: {e}") } ================================================ FILE: testData/exec/tostring_recursion.out ================================================ Caught after depth=99: Native stack overflow ================================================ FILE: testData/exec/type_classes/test_builtin_constructors.nut ================================================ from "types" import * function format_array(arr) { let s = ", ".join(arr) return $"[{s}]" } function format_table(tbl) { let s = ", ".join(tbl.topairs().map(@(e) $"[{e[0]}]={e[1]}")) return $"\{{s}\}" } println("Integer():", Integer()) println("Integer(42):", Integer(42)) println("Integer(3.14):", Integer(3.14)) println("Integer(\"100\"):", Integer("100")) println("Integer(true):", Integer(true)) println() println("Float():", Float()) println("Float(42):", Float(42)) println("Float(3.14):", Float(3.14)) println("Float(\"3.14\"):", Float("3.14")) println("Float(true):", Float(true)) println() println("Bool():", Bool()) println("Bool(0):", Bool(0)) println("Bool(1):", Bool(1)) println("Bool(3.14):", Bool(3.14)) println("Bool(null):", Bool(null)) println("Bool(\"hello\"):", Bool("hello")) println("Bool(\"\"):", Bool("")) println() println("String(42):", String(42)) println("String(3.14):", String(3.14)) println("String(true):", String(true)) println("String(null):", String(null)) println() let arr1 = Array(0) println("Array(0):", format_array(arr1)) let arr2 = Array(5) println("Array(5):", format_array(arr2)) let arr3 = Array(3, "hello") println("Array(3, \"hello\"):", format_array(arr3)) println() let tbl = Table() println("Table():", format_table(tbl)) tbl.x <- 10 tbl.y <- 20 println("After adding keys:", format_table(tbl)) println() println("Null():", Null()) println() let obj = {foo = "bar"} let wr = WeakRef(obj) println("type(WeakRef(obj)):", type(wr)) println("wr.ref():", format_table(wr.ref())) println() let x = Integer("42") println("x = Integer(\"42\"):", x, "type:", typeof(x)) println("x instanceof Integer:", x instanceof Integer) ================================================ FILE: testData/exec/type_classes/test_builtin_constructors.out ================================================ Integer(): 0 Integer(42): 42 Integer(3.14): 3 Integer("100"): 100 Integer(true): 1 Float(): 0 Float(42): 42 Float(3.14): 3.14 Float("3.14"): 3.14 Float(true): 1 Bool(): false Bool(0): false Bool(1): true Bool(3.14): true Bool(null): false Bool("hello"): true Bool(""): true String(42): 42 String(3.14): 3.14 String(true): true String(null): null Array(0): [] Array(5): [null, null, null, null, null] Array(3, "hello"): [hello, hello, hello] Table(): {} After adding keys: {[y]=20, [x]=10} Null(): null type(WeakRef(obj)): weakref wr.ref(): {[foo]=bar} x = Integer("42"): 42 type: integer x instanceof Integer: true ================================================ FILE: testData/exec/type_classes/test_inheritance_error.nut ================================================ from "types" import * try { class MyInt(Integer) { constructor() {} } } catch(e) { println(e) } ================================================ FILE: testData/exec/type_classes/test_inheritance_error.out ================================================ Cannot inherit from built-in type 'integer' ================================================ FILE: testData/exec/type_classes/test_unified_types.nut ================================================ from "types" import * println("\nTest: instanceof with built-in types") println(" 5 instanceof Integer:", 5 instanceof Integer) println(" 3.14 instanceof Float:", 3.14 instanceof Float) println(" true instanceof Bool:", true instanceof Bool) println(" \"hello\" instanceof String:", "hello" instanceof String) println(" [1,2,3] instanceof Array:", [1,2,3] instanceof Array) println(" {a=1} instanceof Table:", {a=1} instanceof Table) println(" null instanceof Null:", null instanceof Null) println("\nTest: Cross-type checks (should be false)") println(" 5 instanceof String:", 5 instanceof String) println(" \"hello\" instanceof Integer:", "hello" instanceof Integer) println("\nTest: classof() function") println(" classof(5)==Integer:", classof(5)==Integer) println(" classof(\"hi\")==String:", classof("hi")==String) println(" classof(true)==Bool:", classof(true)==Bool) println(" classof([])==Array:", classof([])==Array) println("\nTest: Type methods still work") println(" \"hello\".len():", "hello".len()) println(" [1,2,3].len():", [1,2,3].len()) ================================================ FILE: testData/exec/type_classes/test_unified_types.out ================================================ Test: instanceof with built-in types 5 instanceof Integer: true 3.14 instanceof Float: true true instanceof Bool: true "hello" instanceof String: true [1,2,3] instanceof Array: true {a=1} instanceof Table: true null instanceof Null: true Test: Cross-type checks (should be false) 5 instanceof String: false "hello" instanceof Integer: false Test: classof() function classof(5)==Integer: true classof("hi")==String: true classof(true)==Bool: true classof([])==Array: true Test: Type methods still work "hello".len(): 5 [1,2,3].len(): 3 ================================================ FILE: testData/exec/type_hints/function_types.nut ================================================ function fn_def(x: int, y: float = -100.0): float { return x * y } function fn(x: int, y: float): float { return x + y } function apply(func: function|null, x: int, y: float): float|null { return fn?(x, y) } function va(x: bool, ...: string|null) { println(x, vargv[0]) } println(fn_def(10)) println(apply(fn, 5, 10.25)) va(true, "abc", "", null, "342") ================================================ FILE: testData/exec/type_hints/function_types.out ================================================ -1000 15.25 true abc ================================================ FILE: testData/exec/type_hints/var_decl.nut ================================================ let i: int = 5 let t: table = {a = [1, null], b = "abc"} let { a: array|null, b: string|null = "" } = t let [v0: int|null = -999, v1: int|null] = a println(i) println(type(a), b) println(v0, v1) ================================================ FILE: testData/exec/type_hints/var_decl.out ================================================ 5 array abc 1 null ================================================ FILE: testData/exec/type_inference/test_arithmetic_ok.nut ================================================ // EXPECTED: no error - arithmetic on ints produces int local a: int = 10 local b: int = 20 local c: int = a + b local d: int = a * b local e: int = a - b local f: int = a / b local g: int = a % b return [c, d, e, f, g] ================================================ FILE: testData/exec/type_inference/test_arithmetic_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_array_literal_ok.nut ================================================ // EXPECTED: no error - array literal assigned to array variable local x: array = [1, 2, 3] local y: array|null = null local z: array|null = ["a", "b"] return [x, y, z] ================================================ FILE: testData/exec/type_inference/test_array_literal_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_assignment_chain_ok.nut ================================================ let x: int = 234 let y = x + 4 let z = 120.0 * y let w = z let f: float = w ================================================ FILE: testData/exec/type_inference/test_assignment_chain_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_call_result_ok.nut ================================================ // EXPECTED: no error - function return type matches variable type function sqri(x: int): int { return x * x } local y: int = sqri(5) local z: int|null = sqri(5) local w: int|string = sqri(5) return [y, z, w] ================================================ FILE: testData/exec/type_inference/test_call_result_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_call_union_return_ok.nut ================================================ // EXPECTED: no error - fn returns string|int, var accepts string|int|null function fn(val): string|int { return val ? 42 : "hello" } local x: string|int = fn(1) local y: string|int|null = fn(1) return [x, y] ================================================ FILE: testData/exec/type_inference/test_call_union_return_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_class_literal_ok.nut ================================================ // EXPECTED: no error - class literal assigned to class variable local x: class = class { a = 1 b = 2 } local y: class|null = null return [x, y] ================================================ FILE: testData/exec/type_inference/test_class_literal_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_comparison_ok.nut ================================================ // EXPECTED: no error - comparisons produce bool local a: int = 10 local b: int = 20 local c: bool = a > b local d: bool = a < b local e: bool = a == b local f: bool = a != b local g: bool = a >= b local h: bool = a <= b local MyClass = class { x = 0 } local obj = MyClass() local i: bool = obj instanceof MyClass local j: bool = !c return [c, d, e, f, g, h, i, j] ================================================ FILE: testData/exec/type_inference/test_comparison_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_complex_assign_ok.nut ================================================ // EXPECTED: no error // Variable with type annotation reassigned with correct type local x: int = 42 x = 100 x = 1 + 2 * 3 return null ================================================ FILE: testData/exec/type_inference/test_complex_assign_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_complex_bitwise_ok.nut ================================================ // EXPECTED: no error // Bitwise operations always produce int local a: int = 0xFF local b: int = 0x0F local c: int = 0xAA local d: int = 0x55 local result: int = (a & b) | (c ^ d) << 2 local neg: int = ~a local shr: int = a >>> 4 return [result, neg, shr] ================================================ FILE: testData/exec/type_inference/test_complex_bitwise_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_complex_call_chain_ok.nut ================================================ // EXPECTED: no error - chained function calls, final result is float, assigned to float function to_float(x: int): float { return x.tofloat() } function double_it(x: float): float { return x * 2.0 } local result: float = double_it(to_float(42)) return result ================================================ FILE: testData/exec/type_inference/test_complex_call_chain_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_complex_deep_nesting_ok.nut ================================================ // EXPECTED: no error // Deeply nested expression where types work out // ((((1 + 2) * 3) > 5) ? "big" : "small") => string (both branches are string) local a: int = 1 local b: int = 2 local c: int = 3 local deep_result: string = (((a + b) * c) > 5) ? "big" : "small" return deep_result ================================================ FILE: testData/exec/type_inference/test_complex_deep_nesting_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_complex_logical_ok.nut ================================================ // EXPECTED: no error // Logical operators: || returns one of the operands // true || false => bool|bool = bool local x: bool = true || false local y: int|string = 42 || "hello" return [x, y] ================================================ FILE: testData/exec/type_inference/test_complex_logical_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_complex_mixed_arithmetic_ok.nut ================================================ // EXPECTED: no error // (1 + 2) * 3 => int, typeof "x" => string => ternary is int|string function get_flag(): bool { return true } local n: int = 10 local x: int|string = get_flag() ? ((1 + 2) * 3 + n) : typeof "x" return null ================================================ FILE: testData/exec/type_inference/test_complex_mixed_arithmetic_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_complex_nested_ternary_ok.nut ================================================ // EXPECTED: no error - nested ternaries produce int|float|string which fits the declared type local flag1 = true local flag2 = false local x: int|float|string = flag1 ? (flag2 ? 42 : 3.14) : "hello" return x ================================================ FILE: testData/exec/type_inference/test_complex_nested_ternary_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_complex_nullcoalesce_ok.nut ================================================ // EXPECTED: no error // null-coalescing: lhs is int|null, rhs is int // result type: int (non-null part of lhs) | int (rhs) = int function maybe_int(): int|null { return 42 } local x: int = maybe_int() ?? 0 return null ================================================ FILE: testData/exec/type_inference/test_complex_nullcoalesce_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_complex_pure_chain_ok.nut ================================================ // EXPECTED: no error // Pure function results used correctly const function [pure] sqri(x: int): int { return x * x } const function [pure] sqrf(x: float): float { return x * x } function fn(val): string|int { return val ? sqri(val) : "error" } local a: int = sqri(5) local b: float = sqrf(3.14) local c: string|int = fn(42) local d: string|int|null|function = fn(100) return [a, b, c, d] ================================================ FILE: testData/exec/type_inference/test_complex_pure_chain_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_complex_return_expr_ok.nut ================================================ // EXPECTED: no error // Complex return expression: all branches produce int function compute(flag: bool, x: int, y: int): int { return flag ? x * y + 1 : x - y } return compute ================================================ FILE: testData/exec/type_inference/test_complex_return_expr_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_complex_table_ternary_ok.nut ================================================ // EXPECTED: no error // Ternary produces table|array, declared as table|array function get_data(flag: bool): table|array { return flag ? { x = 1 } : [1, 2, 3] } return get_data ================================================ FILE: testData/exec/type_inference/test_complex_table_ternary_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_const_array4_ok.nut ================================================ const ARR1 = [1, null] const ARR2 = [4, 3] function fn(x: table|array|int = true ? ARR1 : {}): null {} return fn({}) ================================================ FILE: testData/exec/type_inference/test_const_array4_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_const_array_ok.nut ================================================ const ARR1 = [1, null] const ARR2 = [4, 3] let x: array = true ? ARR1 : ARR2 return x ================================================ FILE: testData/exec/type_inference/test_const_array_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_const_function_ok.nut ================================================ const function fn1() {} const function fn2() {} let x: function = true ? fn1 : fn2 return x ================================================ FILE: testData/exec/type_inference/test_const_function_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_const_inline_ok.nut ================================================ // EXPECTED: no error - const values match declared types const MY_INT = 42 const MY_STR = "hello" const MY_FLOAT = 3.14 const MY_BOOL = true local a: int = MY_INT local b: string = MY_STR local c: float = MY_FLOAT local d: bool = MY_BOOL local e: int|string = MY_INT local f: int|string = MY_STR return [a, b, c, d, e, f] ================================================ FILE: testData/exec/type_inference/test_const_inline_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_const_table_ok.nut ================================================ const TAB1 = {} const TAB2 = {x = 4} let x: table = true ? TAB1 : TAB2 return x ================================================ FILE: testData/exec/type_inference/test_const_table_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_destructure_array_default_ok.nut ================================================ // EXPECTED: no error - default values match declared types let [a: int = 10, b: string = "default", c: bool = true] = [1, "actual", false] return [a, b, c] ================================================ FILE: testData/exec/type_inference/test_destructure_array_default_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_destructure_array_ok.nut ================================================ // EXPECTED: no error - destructured values with defaults match declared types let [x: int = 4, y: float = 6.0] = [100, 200.4] return [x, y] ================================================ FILE: testData/exec/type_inference/test_destructure_array_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_destructure_from_function_ok.nut ================================================ // EXPECTED: no error - destructuring from function result (runtime check only) function make_pair(): table { return {x = 10, y = 20} } let {x: int, y: int} = make_pair() return [x, y] ================================================ FILE: testData/exec/type_inference/test_destructure_from_function_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_destructure_nested_ok.nut ================================================ // EXPECTED: no error - nested destructuring with types let {config} = {config = {width = 800, height = 600}} let {width: int, height: int} = config return [width, height] ================================================ FILE: testData/exec/type_inference/test_destructure_nested_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_destructure_table_ok.nut ================================================ // EXPECTED: no error - destructured values match declared types let {x: int, y: string} = {x = 42, y = "hello"} let {a: int|null, b: float} = {a = null, b = 3.14} return [x, y, a, b] ================================================ FILE: testData/exec/type_inference/test_destructure_table_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_freeze_ok.nut ================================================ // EXPECTED: no error - freeze() preserves type of its argument local a: table = freeze({x = 1}) local b: array = freeze([1, 2, 3]) return [a, b] ================================================ FILE: testData/exec/type_inference/test_freeze_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_function_literal_ok.nut ================================================ // EXPECTED: no error - lambda assigned to function variable local x: function = @() 42 local y: function|null = null local z: function|null = @(a, b) a + b return [x, y, z] ================================================ FILE: testData/exec/type_inference/test_function_literal_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_instance_from_class_ok.nut ================================================ // EXPECTED: no error - class() produces instance, assigned to instance // Note: calling a class produces an instance, but inferring this // requires knowing the callee is a class, which may be ~0u (unknown) // in early implementation. This test documents the desired behavior. local MyClass = class { x = 0 constructor() {} } local obj: instance = MyClass() local obj2: instance|null = null return [obj, obj2] ================================================ FILE: testData/exec/type_inference/test_instance_from_class_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_literal_ok.nut ================================================ // EXPECTED: no error - all types match local a: int = 42 local b: float = 3.14 local c: string = "hello" local d: bool = true local e: int|null = null local f: int|string = 42 local g: int|string = "world" local h: null = null return [a, b, c, d, e, f, g, h] ================================================ FILE: testData/exec/type_inference/test_literal_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_return_literal_ok.nut ================================================ // EXPECTED: no error - return type matches function fn(): int { return 42 } function fn2(): string { return "hello" } function fn3(): int|string { return 42 } function fn4(): int|string { return "hello" } return [fn(), fn2(), fn3(), fn4()] ================================================ FILE: testData/exec/type_inference/test_return_literal_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_string_concat_ok.nut ================================================ // EXPECTED: no error - string + anything produces string local s: string = "hello" local n: int = 42 local result: string = s + n local result2: string = s + " world" return [result, result2] ================================================ FILE: testData/exec/type_inference/test_string_concat_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_table_literal_ok.nut ================================================ // EXPECTED: no error - table literal assigned to table variable local x: table = { a = 1, b = 2, c = "hello" } local y: table|null = null local z: table|null = { key = "value" } return [x, y, z] ================================================ FILE: testData/exec/type_inference/test_table_literal_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_table_return_ok.nut ================================================ // EXPECTED: no error function make_table(): table { return { a = 1, b = 2 } } function make_array(): array { return [1, 2, 3] } function make_either(flag: bool): table|array { return flag ? { a = 1 } : [1, 2] } return [make_table(), make_array(), make_either(true)] ================================================ FILE: testData/exec/type_inference/test_table_return_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_ternary_ok.nut ================================================ // EXPECTED: no error - ternary produces int|int = int local flag = true local x: int = flag ? 42 : 100 return null ================================================ FILE: testData/exec/type_inference/test_ternary_ok.out ================================================ ================================================ FILE: testData/exec/type_inference/test_typeof_ok.nut ================================================ // EXPECTED: no error - typeof always returns string local x: string = typeof 42 local y: string = typeof "hello" local z: string = typeof null return [x, y, z] ================================================ FILE: testData/exec/type_inference/test_typeof_ok.out ================================================ ================================================ FILE: testData/exec/valid_syntax/arrays.nut ================================================ // Valid syntax: Arrays // Empty array let empty = [] assert(empty.len() == 0) // Array with values let arr = [1, 2, 3, 4, 5] assert(arr.len() == 5) assert(arr[0] == 1) assert(arr[4] == 5) // Mixed types let mixed = [1, "hello", 3.14, null, true, [1, 2], {a = 1}] assert(mixed.len() == 7) assert(typeof mixed[0] == "integer") assert(typeof mixed[1] == "string") assert(typeof mixed[5] == "array") assert(typeof mixed[6] == "table") // Nested arrays let nested = [[1, 2], [3, 4], [5, 6]] assert(nested[0][0] == 1) assert(nested[2][1] == 6) // Array methods - append local a = [1, 2, 3] a.append(4) assert(a.len() == 4) assert(a[3] == 4) // Multiple append a.append(5, 6) assert(a.len() == 6) // extend local b = [1, 2] b.extend([3, 4]) assert(b.len() == 4) // pop let popped = b.pop() assert(popped == 4) assert(b.len() == 3) // top assert(b.top() == 3) // insert b.insert(0, 99) assert(b[0] == 99) // remove let removed = b.remove(0) assert(removed == 99) // resize local resizable = [1, 2, 3] resizable.resize(5, 0) assert(resizable.len() == 5) assert(resizable[3] == 0) // sort local sortable = [3, 1, 4, 1, 5, 9, 2, 6] sortable.sort() assert(sortable[0] == 1) assert(sortable[sortable.len() - 1] == 9) // sort with custom comparator local desc = [3, 1, 4, 1, 5] desc.sort(@(a, b) b <=> a) assert(desc[0] == 5) assert(desc[desc.len() - 1] == 1) // reverse local rev = [1, 2, 3] rev.reverse() assert(rev[0] == 3) assert(rev[2] == 1) // slice let sliced = [1, 2, 3, 4, 5].slice(1, 4) assert(sliced.len() == 3) assert(sliced[0] == 2) assert(sliced[2] == 4) // slice with default end let sliced2 = [1, 2, 3, 4, 5].slice(2) assert(sliced2.len() == 3) assert(sliced2[0] == 3) // filter let evens = [1, 2, 3, 4, 5, 6].filter(@(v) v % 2 == 0) assert(evens.len() == 3) assert(evens[0] == 2) // map let doubled = [1, 2, 3].map(@(v) v * 2) assert(doubled[0] == 2) assert(doubled[1] == 4) assert(doubled[2] == 6) // reduce let sum = [1, 2, 3, 4, 5].reduce(@(acc, v) acc + v, 0) assert(sum == 15) // each local each_sum = 0 let _ = [1, 2, 3].each(@(v) each_sum += v) assert(each_sum == 6) // findindex let found = [10, 20, 30, 40].findindex(@(v) v > 25) assert(found == 2) // findvalue let fval = [10, 20, 30, 40].findvalue(@(v) v > 25) assert(fval == 30) // indexof let idx = [10, 20, 30].indexof(20) assert(idx == 1) // contains assert([1, 2, 3].contains(2)) assert(![1, 2, 3].contains(4)) // clone let orig = [1, 2, 3] let copy = clone orig copy[0] = 99 assert(orig[0] == 1) // clear local clearable = [1, 2, 3] clearable.clear() assert(clearable.len() == 0) // swap local swappable = [1, 2, 3] swappable.swap(0, 2) assert(swappable[0] == 3) assert(swappable[2] == 1) // hasindex assert([1, 2, 3].hasindex(0)) assert([1, 2, 3].hasindex(2)) assert(![1, 2, 3].hasindex(3)) // apply (modify in-place) local applyable = [1, 2, 3] applyable.apply(@(v) v * 10) assert(applyable[0] == 10) assert(applyable[1] == 20) // Iteration local iter_sum = 0 foreach (v in [10, 20, 30]) { iter_sum += v } assert(iter_sum == 60) // Iteration with index local indices = [] foreach (i, v in ["a", "b", "c"]) { indices.append(i) } assert(indices[0] == 0) assert(indices[2] == 2) print("arrays: OK\n") ================================================ FILE: testData/exec/valid_syntax/arrays.out ================================================ arrays: OK ================================================ FILE: testData/exec/valid_syntax/classes.nut ================================================ // Valid syntax: Classes and inheritance // Basic class class Point { x = 0 y = 0 } let p = Point() assert(p.x == 0) assert(p.y == 0) p.x = 10 p.y = 20 assert(p.x == 10) // Class with constructor class Point2 { x = 0 y = 0 constructor(x, y) { this.x = x this.y = y } } let p2 = Point2(3, 4) assert(p2.x == 3) assert(p2.y == 4) // Class with methods class Circle { r = 0 constructor(r) { this.r = r } function area() { return 3.14159 * this.r * this.r } function perimeter() { return 2.0 * 3.14159 * this.r } } let c = Circle(5) assert(c.r == 5) // Class with static members class Config { static VERSION = "1.0" static COUNT = 0 } assert(Config.VERSION == "1.0") // Inheritance using parenthesis syntax class Shape { function area() { return 0 } function name() { return "shape" } } class Rect(Shape) { w = 0 h = 0 constructor(w, h) { this.w = w this.h = h } function area() { return this.w * this.h } function name() { return "rect" } } let r = Rect(3, 4) assert(r.area() == 12) assert(r.name() == "rect") assert(r instanceof Rect) assert(r instanceof Shape) // Inheritance with base call class Base { value = 0 constructor(v = 0) { this.value = v } function get() { return this.value } } class Derived(Base) { extra = 0 constructor(v, e) { base.constructor(v) this.extra = e } function get() { return base.get() + this.extra } } let d = Derived(10, 5) assert(d.get() == 15) assert(d.value == 10) assert(d.extra == 5) // getclass and getbase assert(d.getclass() == Derived) assert(Derived.getbase() == Base) // Class as expression let MyClass = class { value = 42 function get() { return this.value } } let mc = MyClass() assert(mc.get() == 42) // Class with metamethods - use getclass() for self-reference class Vector { x = 0 y = 0 constructor(x, y) { this.x = x this.y = y } function _add(other) { return this.getclass()(this.x + other.x, this.y + other.y) } function _sub(other) { return this.getclass()(this.x - other.x, this.y - other.y) } function _cmp(other) { let d1 = this.x * this.x + this.y * this.y let d2 = other.x * other.x + other.y * other.y if (d1 < d2) return -1 if (d1 > d2) return 1 return 0 } function _tostring() { return $"Vector({this.x}, {this.y})" } function _typeof() { return "Vector" } } let v1 = Vector(1, 2) let v2 = Vector(3, 4) let v3 = v1 + v2 assert(v3.x == 4) assert(v3.y == 6) let v4 = v2 - v1 assert(v4.x == 2) assert(v4.y == 2) assert(typeof v1 == "Vector") assert(v1 < v2) assert(!(v2 < v1)) // Class modification before instantiation class Modifiable { x = 1 } Modifiable.y <- 2 let mod_inst = Modifiable() assert(mod_inst.x == 1) assert(mod_inst.y == 2) // Constructor with default params class WithDefaults { x = 0 y = 0 constructor(x = 10, y = 20) { this.x = x this.y = y } } let wd1 = WithDefaults() assert(wd1.x == 10) assert(wd1.y == 20) let wd2 = WithDefaults(1, 2) assert(wd2.x == 1) assert(wd2.y == 2) // Multiple inheritance levels class A { function who() { return "A" } } class B(A) { function who() { return "B" } } class C(B) { function who() { return "C" } } let obj_c = C() assert(obj_c.who() == "C") assert(obj_c instanceof A) assert(obj_c instanceof B) assert(obj_c instanceof C) // rawin/rawget/rawset on instances let inst = Point2(1, 2) assert(inst.rawin("x")) assert(inst.rawget("x") == 1) inst.rawset("x", 99) assert(inst.x == 99) print("classes: OK\n") ================================================ FILE: testData/exec/valid_syntax/classes.out ================================================ classes: OK ================================================ FILE: testData/exec/valid_syntax/closures_scope.nut ================================================ // Valid syntax: Closures and scoping // Basic closure local outer_val = 10 let closure = function() { return outer_val } assert(closure() == 10) // Closure captures by reference outer_val = 20 assert(closure() == 20) // Nested closures function make_multiplier(factor) { return function(x) { return x * factor } } let double_fn = make_multiplier(2) let triple_fn = make_multiplier(3) assert(double_fn(5) == 10) assert(triple_fn(5) == 15) // Closure over loop variable local fns = [] for (local i = 0; i < 5; i++) { let captured_i = i // capture by value fns.append(@() captured_i) } assert(fns[0]() == 0) assert(fns[4]() == 4) // Nested function accessing outer scope function outer() { local x = 100 function inner() { return x + 1 } return inner() } assert(outer() == 101) // Deep nesting function level1() { local a_val = 1 function level2() { local b_val = 2 function level3() { return a_val + b_val } return level3() } return level2() } assert(level1() == 3) // Function returning table of closures function make_stack() { local data = [] return { function push(v) { data.append(v) } function pop() { return data.pop() } function size() { return data.len() } function peek() { return data.top() } } } let stack = make_stack() stack.push(1) stack.push(2) stack.push(3) assert(stack.size() == 3) assert(stack.peek() == 3) assert(stack.pop() == 3) assert(stack.size() == 2) // IIFE (Immediately Invoked Function Expression) let iife_result = (function() { return 42 })() assert(iife_result == 42) // Lambda IIFE let lambda_iife = (@() 99)() assert(lambda_iife == 99) // Block scope isolation { local scoped = 42 assert(scoped == 42) } // Multiple blocks { local a_block = 1 { local b_block = 2 assert(a_block == 1) assert(b_block == 2) } } print("closures_scope: OK\n") ================================================ FILE: testData/exec/valid_syntax/closures_scope.out ================================================ closures_scope: OK ================================================ FILE: testData/exec/valid_syntax/control_flow.nut ================================================ // Valid syntax: Control flow statements // if/else local result = null if (true) result = "yes" assert(result == "yes") if (false) result = "no" else result = "else" assert(result == "else") // if/else if/else chain local val = 2 if (val == 1) result = "one" else if (val == 2) result = "two" else if (val == 3) result = "three" else result = "other" assert(result == "two") // if with block if (true) { result = "block" } assert(result == "block") // if with local declaration in condition if (local cond_val = 42) { assert(cond_val == 42) } // while loop local count = 0 while (count < 5) { count++ } assert(count == 5) // do/while loop local dw_count = 0 do { dw_count++ } while (dw_count < 3) assert(dw_count == 3) // do/while executes at least once local dw_once = 0 do { dw_once++ } while (false) assert(dw_once == 1) // for loop local sum = 0 for (local i = 0; i < 5; i++) { sum += i } assert(sum == 10) // for loop with empty parts local inf_count = 0 for (;;) { inf_count++ if (inf_count >= 3) break } assert(inf_count == 3) // foreach with array let arr = [10, 20, 30] local arr_sum = 0 foreach (v in arr) { arr_sum += v } assert(arr_sum == 60) // foreach with index and value local idx_sum = 0 foreach (i, v in arr) { idx_sum += i } assert(idx_sum == 3) // 0+1+2 // foreach with table let tbl = {a = 1, b = 2, c = 3} local tbl_sum = 0 foreach (k, v in tbl) { tbl_sum += v } assert(tbl_sum == 6) // foreach with string local char_count = 0 foreach (ch in "hello") { char_count++ } assert(char_count == 5) // break in loop local break_val = 0 for (local i = 0; i < 100; i++) { if (i == 5) break break_val = i } assert(break_val == 4) // continue in loop local cont_sum = 0 for (local i = 0; i < 10; i++) { if (i % 2 == 0) continue cont_sum += i } assert(cont_sum == 25) // 1+3+5+7+9 // break in while local bw = 0 while (true) { bw++ if (bw >= 3) break } assert(bw == 3) // continue in while local cw = 0 local cw_sum = 0 while (cw < 10) { cw++ if (cw % 2 == 0) continue cw_sum += cw } assert(cw_sum == 25) // break in foreach local bf_last = 0 foreach (i, v in [10, 20, 30, 40, 50]) { if (v == 30) break bf_last = v } assert(bf_last == 20) // Nested loops with break local nested_count = 0 for (local i = 0; i < 5; i++) { for (local j = 0; j < 5; j++) { if (j == 2) break nested_count++ } } assert(nested_count == 10) // switch statement #allow-switch-statement local sw_val = 2 local sw_result = null switch (sw_val) { case 1: sw_result = "one" break case 2: sw_result = "two" break case 3: sw_result = "three" break default: sw_result = "default" break } assert(sw_result == "two") // switch with default sw_val = 99 switch (sw_val) { case 1: sw_result = "one" break default: sw_result = "default" break } assert(sw_result == "default") print("control_flow: OK\n") ================================================ FILE: testData/exec/valid_syntax/control_flow.out ================================================ control_flow: OK ================================================ FILE: testData/exec/valid_syntax/destructuring.nut ================================================ // Valid syntax: Destructuring // Array destructuring let [a, b, c] = [10, 20, 30] assert(a == 10) assert(b == 20) assert(c == 30) // Table/object destructuring let {x, y} = {x = 100, y = 200} assert(x == 100) assert(y == 200) // Destructuring with default values let {p = 1, q = 2} = {p = 10} assert(p == 10) assert(q == 2) // Destructuring in function parameters - table function process_point({x, y}) { return x + y } assert(process_point({x = 3, y = 4}) == 7) // Destructuring in function parameters - array function process_pair([a, b]) { return a * b } assert(process_pair([3, 4]) == 12) // Destructuring with type annotations let {m: int, n: int} = {m = 5, n = 10} assert(m == 5) assert(n == 10) // Function parameter destructuring with defaults function with_defaults({name = "default", value = 0}) { return $"{name}={value}" } assert(with_defaults({name = "test", value = 42}) == "test=42") assert(with_defaults({}) == "default=0") // Multiple destructurings let {r} = {r = 99} assert(r == 99) // Partial array destructuring let [first, second] = [1, 2, 3, 4, 5] assert(first == 1) assert(second == 2) // Table destructuring with multiple fields let {name, age, active} = {name = "John", age = 30, active = true} assert(name == "John") assert(age == 30) assert(active == true) print("destructuring: OK\n") ================================================ FILE: testData/exec/valid_syntax/destructuring.out ================================================ destructuring: OK ================================================ FILE: testData/exec/valid_syntax/enums_consts.nut ================================================ // Valid syntax: Enums and constants // Basic enum enum Color { RED, GREEN, BLUE } assert(Color.RED == 0) assert(Color.GREEN == 1) assert(Color.BLUE == 2) // Enum with explicit values enum HttpCode { OK = 200, NOT_FOUND = 404, ERROR = 500 } assert(HttpCode.OK == 200) assert(HttpCode.NOT_FOUND == 404) // Enum with mixed auto and explicit values // Note: auto-increment always starts from 0 for each member without explicit value enum Mixed { A, // 0 B = 10, C, // 1 (resets from 0-based counter) D = 20 } assert(Mixed.A == 0) assert(Mixed.B == 10) assert(Mixed.C == 1) assert(Mixed.D == 20) // Enum with string values enum Names { FIRST = "first", SECOND = "second" } assert(Names.FIRST == "first") // Enum with float values enum Precision { LOW = 0.1, MEDIUM = 0.01, HIGH = 0.001 } // const declaration const MAX_SIZE = 100 const PI_APPROX = 3.14159 const GREETING = "hello" assert(MAX_SIZE == 100) assert(PI_APPROX == 3.14159) assert(GREETING == "hello") // Computed const const DOUBLE_MAX = MAX_SIZE * 2 assert(DOUBLE_MAX == 200) // global const global const GLOBAL_VAL = 42 // global enum global enum Direction { NORTH, SOUTH, EAST, WEST } assert(Direction.NORTH == 0) assert(Direction.WEST == 3) // Using enum values in expressions let color = Color.RED if (color == Color.RED) { assert(true) } // Enum in switch #allow-switch-statement local result = "" let status = HttpCode.NOT_FOUND switch (status) { case HttpCode.OK: result = "ok" break case HttpCode.NOT_FOUND: result = "not found" break case HttpCode.ERROR: result = "error" break } assert(result == "not found") print("enums_consts: OK\n") ================================================ FILE: testData/exec/valid_syntax/enums_consts.out ================================================ enums_consts: OK ================================================ FILE: testData/exec/valid_syntax/error_handling.nut ================================================ // Valid syntax: Error handling (try/catch/throw) // Basic try/catch local caught = null try { throw "test error" } catch (e) { caught = e } assert(caught == "test error") // Catching runtime errors local rt_caught = false try { let t = {} let _ = t.nonexistent_method() } catch (e) { rt_caught = true } assert(rt_caught) // Throw integer local int_err = null try { throw 42 } catch (e) { int_err = e } assert(int_err == 42) // Throw null local null_err = "not null" try { throw null } catch (e) { null_err = e } assert(null_err == null) // Throw table local tbl_err = null try { throw {code = 404, message = "Not found"} } catch (e) { tbl_err = e } assert(tbl_err.code == 404) // Nested try/catch local outer_caught = null local inner_caught = null try { try { throw "inner" } catch (e) { inner_caught = e throw "outer" } } catch (e) { outer_caught = e } assert(inner_caught == "inner") assert(outer_caught == "outer") // Try/catch with no exception local no_err = true try { let x = 42 } catch (e) { no_err = false } assert(no_err) // Function that may throw function divide(a, b) { if (b == 0) throw "division by zero" return a / b } assert(divide(10, 2) == 5) local div_err = null try { divide(10, 0) } catch (e) { div_err = e } assert(div_err == "division by zero") // Try/catch in loop local errors_caught = 0 for (local i = 0; i < 5; i++) { try { if (i % 2 == 0) throw "even error" } catch (e) { errors_caught++ } } assert(errors_caught == 3) // 0, 2, 4 // Rethrow local rethrown = null try { try { throw "original" } catch (e) { throw e // rethrow } } catch (e) { rethrown = e } assert(rethrown == "original") print("error_handling: OK\n") ================================================ FILE: testData/exec/valid_syntax/error_handling.out ================================================ error_handling: OK ================================================ FILE: testData/exec/valid_syntax/functions.nut ================================================ // Valid syntax: Functions // Basic function declaration function add(a, b) { return a + b } assert(add(2, 3) == 5) // local function local function subtract(a, b) { return a - b } assert(subtract(10, 3) == 7) // let function let multiply = function(a, b) { return a * b } assert(multiply(3, 4) == 12) // Named function expression let factorial = function factorial(n) { if (n <= 1) return 1 return n * factorial(n - 1) } assert(factorial(5) == 120) // Lambda expressions let square = @(x) x * x assert(square(5) == 25) let add_lambda = @(a, b) a + b assert(add_lambda(2, 3) == 5) // Lambda with function wrapper for multi-statement let complex_fn = function(x) { local result = x * 2 return result + 1 } assert(complex_fn(5) == 11) // Default parameters function greet(name = "World") { return "Hello " + name } assert(greet() == "Hello World") assert(greet("Quirrel") == "Hello Quirrel") // Multiple default parameters function multi_def(a, b = 10, c = 20) { return a + b + c } assert(multi_def(1) == 31) assert(multi_def(1, 2) == 23) assert(multi_def(1, 2, 3) == 6) // Variable arguments function sum_all(...) { local total = 0 foreach (v in vargv) total += v return total } assert(sum_all(1, 2, 3, 4, 5) == 15) assert(sum_all() == 0) // Varargs with regular params function first_and_rest(first, ...) { local rest_sum = 0 foreach (v in vargv) rest_sum += v return [first, rest_sum] } let far = first_and_rest(10, 20, 30) assert(far[0] == 10) assert(far[1] == 50) // Function returning function (closure) function make_adder(n) { return @(x) x + n } let add5 = make_adder(5) assert(add5(10) == 15) assert(add5(20) == 25) // Closures capture by reference function make_counter() { local count = 0 return { function inc() { count++ } function get() { return count } } } let counter = make_counter() counter.inc() counter.inc() counter.inc() assert(counter.get() == 3) // Return without value function no_return() { local x = 42 } assert(no_return() == null) // Explicit return null function return_null() { return null } assert(return_null() == null) // Function as value let fn_array = [ @(x) x + 1, @(x) x * 2, @(x) x - 3 ] assert(fn_array[0](5) == 6) assert(fn_array[1](5) == 10) assert(fn_array[2](5) == 2) // Recursive function function fib(n) { if (n <= 1) return n return fib(n - 1) + fib(n - 2) } assert(fib(10) == 55) // Tail recursion function tail_sum(n, acc = 0) { if (n <= 0) return acc return tail_sum(n - 1, acc + n) } assert(tail_sum(100) == 5050) // Function with type annotations function typed_add(x: int, y: int): int { return x + y } assert(typed_add(2, 3) == 5) // Function with union type annotations function nullable_fn(x: int|null = null) { return x ?? 0 } assert(nullable_fn() == 0) assert(nullable_fn(42) == 42) // Lambda with default let def_lambda = @(x = 10) x * 2 assert(def_lambda() == 20) assert(def_lambda(5) == 10) // callee() let self_ref = function() { return callee() } assert(typeof self_ref() == "function") // Function call/acall function test_fn(a, b) { return a + b } assert(test_fn.call(null, 3, 4) == 7) assert(test_fn.acall([null, 3, 4]) == 7) // bindenv let obj = {name = "test"} let bound = function() { return this.name }.bindenv(obj) assert(bound() == "test") print("functions: OK\n") ================================================ FILE: testData/exec/valid_syntax/functions.out ================================================ functions: OK ================================================ FILE: testData/exec/valid_syntax/generators.nut ================================================ // Valid syntax: Generators and yield // Basic generator function count_to(n) { for (local i = 1; i <= n; i++) yield i } // Using resume let gen = count_to(3) assert(resume gen == 1) assert(resume gen == 2) assert(resume gen == 3) assert(resume gen == null) // Generator with foreach local sum = 0 foreach (v in count_to(5)) { sum += v } assert(sum == 15) // 1+2+3+4+5 // Fibonacci generator function fib_gen(n) { local a = 0, b = 1 for (local i = 0; i < n; i++) { yield a let temp = a + b a = b b = temp } } let fibs = [] foreach (f in fib_gen(8)) { fibs.append(f) } assert(fibs[0] == 0) assert(fibs[1] == 1) assert(fibs[7] == 13) // Generator yielding different types function mixed_gen() { yield 42 yield "hello" yield [1, 2, 3] yield {a = 1} } let mg = mixed_gen() assert(resume mg == 42) assert(resume mg == "hello") let arr = resume mg assert(arr.len() == 3) let tbl = resume mg assert(tbl.a == 1) // Empty generator (no yields) function empty_gen() { return null } let eg = empty_gen() // Not really a generator, just a function // Generator with yield and no value function void_gen(n) { for (local i = 0; i < n; i++) yield } let vg = void_gen(3) assert(resume vg == null) assert(resume vg == null) assert(resume vg == null) // Infinite generator (controlled with break) function naturals() { local n = 1 while (true) { yield n n++ } } let nat = naturals() local first_five = [] for (local i = 0; i < 5; i++) { first_five.append(resume nat) } assert(first_five[0] == 1) assert(first_five[4] == 5) // Range generator function range(start, stop, step = 1) { for (local i = start; i < stop; i += step) yield i } local range_vals = [] foreach (v in range(0, 10, 2)) { range_vals.append(v) } assert(range_vals.len() == 5) assert(range_vals[0] == 0) assert(range_vals[4] == 8) print("generators: OK\n") ================================================ FILE: testData/exec/valid_syntax/generators.out ================================================ generators: OK ================================================ FILE: testData/exec/valid_syntax/literals.nut ================================================ // Valid syntax: All literal types // Integer literals let int_dec = 42 let int_neg = -123 let int_zero = 0 let int_hex = 0xFF00A120 let int_hex_lower = 0xff let int_char = 'w' let int_char2 = 'A' let int_sep = 123_456 let int_hex_sep = 0xAB_CD_01_23 // Float literals let f1 = 1.52 let f2 = 1.0 let f3 = 0.234 let f4 = 6.5e-11 let f5 = 1.e2 let f6 = 12.345_67e+2 let f7 = 3.14159 let f8 = 1e10 let f9 = 2.5E3 let f10 = 0.0 // String literals let s1 = "regular string" let s2 = "escape sequences: \t \n \r \\ \" \' \0" let s3 = "hex escape: \x41\x42" let s4 = "unicode: \u0041\u0042" let s5 = @"verbatim string with no escapes \n \t" let s6 = @"verbatim multiline string" let s7 = @"verbatim with ""doubled"" quotes" let s8 = "" let s9 = @"" // String interpolation let x = 42 let interp1 = $"value is {x}" let interp2 = $"calc: {x + 1}" let interp3 = $"nested: {$"inner {x}"}" // Boolean literals let b_true = true let b_false = false // Null literal let n = null // Verify types assert(typeof int_dec == "integer") assert(typeof f1 == "float") assert(typeof s1 == "string") assert(typeof b_true == "bool") assert(typeof n == "null") print("literals: OK\n") ================================================ FILE: testData/exec/valid_syntax/literals.out ================================================ literals: OK ================================================ FILE: testData/exec/valid_syntax/metamethods.nut ================================================ // Valid syntax: Metamethods // _add and _sub - use getclass() for self-reference class Money { amount = 0 constructor(a) { this.amount = a } function _add(other) { return this.getclass()(this.amount + other.amount) } function _sub(other) { return this.getclass()(this.amount - other.amount) } function _mul(factor) { return this.getclass()(this.amount * factor) } function _div(divisor) { return this.getclass()(this.amount / divisor) } function _modulo(m) { return this.getclass()(this.amount % m) } function _unm() { return this.getclass()(-this.amount) } function _cmp(other) { if (this.amount < other.amount) return -1 if (this.amount > other.amount) return 1 return 0 } function _tostring() { return $"${this.amount}" } function _typeof() { return "Money" } } let a = Money(100) let b = Money(50) let sum = a + b assert(sum.amount == 150) let diff = a - b assert(diff.amount == 50) let product = a * 3 assert(product.amount == 300) let neg = -a assert(neg.amount == -100) assert(a > b) assert(b < a) assert(typeof a == "Money") // _get and _set class FlexTable { _data = null constructor() { this._data = {} } function _get(key) { if (key in this._data) return this._data[key] return null } function _set(key, val) { this._data[key] <- val } } let ft = FlexTable() ft.name = "test" ft.value = 42 assert(ft.name == "test") assert(ft.value == 42) assert(ft.missing == null) // _nexti for iteration class Range { _start = 0 _end = 0 constructor(s, e) { this._start = s; this._end = e } function _nexti(prev) { if (prev == null) return this._start if (prev + 1 >= this._end) return null return prev + 1 } function _get(idx) { return idx } } local range_sum = 0 foreach (v in Range(0, 5)) { range_sum += v } assert(range_sum == 10) // 0+1+2+3+4 // _call metamethod class Callable { _value = 0 constructor(v) { this._value = v } function _call(env, arg) { return this._value + arg } } let callable = Callable(10) assert(callable(5) == 15) assert(callable(20) == 30) // _cloned metamethod class Tracked { id = 0 cloned_from = null constructor(id) { this.id = id } function _cloned(orig) { this.cloned_from = orig.id } } let original = Tracked(1) let cloned = clone original assert(cloned.cloned_from == 1) print("metamethods: OK\n") ================================================ FILE: testData/exec/valid_syntax/metamethods.out ================================================ metamethods: OK ================================================ FILE: testData/exec/valid_syntax/misc.nut ================================================ // Valid syntax: Miscellaneous language features #allow-delete-operator // Comments // Single line comment /* Block comment */ /* Multi line comment */ // __LINE__ and __FILE__ let line = __LINE__ assert(typeof line == "integer") let file = __FILE__ assert(typeof file == "string") // Semicolons (optional) let a = 1; let b = 2; let c = 3; assert(a + b + c == 6) // Semicolons can be omitted let d_val = 4 let e_val = 5 assert(d_val + e_val == 9) // Empty blocks if (true) {} while (false) {} for (;;) { break } // Nested expressions let complex = ((1 + 2) * (3 + 4)) / (5 - 2) assert(complex == 7) // Expression precedence assert(2 + 3 * 4 == 14) assert((2 + 3) * 4 == 20) // Type conversion methods (use variables to avoid parse issues) local iv = 42 assert(iv.tofloat() == 42.0) local fv = 3.14 assert(fv.tointeger() == 3) assert(iv.tostring() == "42") assert(65 .tochar() == "A") // assert with message assert(true, "this should pass") // assert with lazy message assert(true, @() "lazy message") // Weakref let obj = {value = 42} let weak = obj.weakref() assert(typeof weak == "weakref") assert(weak.ref().value == 42) // Truthiness rules assert(1) // non-zero int is truthy assert("hello") // non-empty string is truthy assert([]) // empty array is truthy assert({}) // empty table is truthy assert(!null) // null is falsy assert(!false) // false is falsy // Chained method calls let result = [3, 1, 4, 1, 5].sort().reverse() assert(result[0] == 5) // Multiline expressions let multi = 1 + 2 + 3 assert(multi == 6) // Table as namespace let Math = { function abs(x) { return x >= 0 ? x : -x } function max(a, b) { return a > b ? a : b } function min(a, b) { return a < b ? a : b } } assert(Math.abs(-5) == 5) assert(Math.max(3, 7) == 7) assert(Math.min(3, 7) == 3) // Newthread function thread_fn() { return 42 } let t = newthread(thread_fn) assert(typeof t == "thread") // compilestring let compiled = compilestring("return 42") assert(compiled() == 42) // Type method access with .$ let override_tbl = {len = @() 0} assert(override_tbl.len() == 0) // calls override assert(override_tbl.$len() == 1) // calls type method // delete expression returns deleted value local del_tbl = {a = 42, b = 99} let deleted_val = delete del_tbl.a assert(deleted_val == 42) assert(!("a" in del_tbl)) print("misc: OK\n") ================================================ FILE: testData/exec/valid_syntax/misc.out ================================================ misc: OK ================================================ FILE: testData/exec/valid_syntax/null_safety.nut ================================================ // Valid syntax: Null safety operators // Null-conditional member access let obj = {name = "test", nested = {value = 42}} assert(obj?.name == "test") assert(obj?.nested?.value == 42) let null_obj = null assert(null_obj?.name == null) assert(null_obj?.nested?.value == null) // Null-conditional indexing let arr = [1, 2, 3] assert(arr?[0] == 1) assert(arr?[2] == 3) let null_arr = null assert(null_arr?[0] == null) // Null-conditional with table bracket let tbl = {a = 1, b = 2} assert(tbl?["a"] == 1) let null_tbl = null assert(null_tbl?["a"] == null) // Null-coalescing operator assert((null ?? "default") == "default") assert(("value" ?? "default") == "value") assert((0 ?? "default") == 0) // 0 is not null assert((false ?? "default") == false) // false is not null assert(("" ?? "default") == "") // empty string is not null // Chained null-coalescing assert((null ?? null ?? "last") == "last") assert((null ?? "middle" ?? "last") == "middle") // Null-safe function call function greet(name) { return "Hello " + name } let fn = greet assert(fn?("World") == "Hello World") let null_fn = null assert(null_fn?("World") == null) // Null propagation through chain let deep = {level1 = {level2 = {level3 = "found"}}} assert(deep?.level1?.level2?.level3 == "found") assert(deep?.missing?.level2?.level3 == null) // Null-safe type method access let s = "hello" assert(s?.$len() == 5) let null_s = null assert(null_s?.$len() == null) // Practical patterns function safe_get(tbl, key, default_val = null) { return tbl?[key] ?? default_val } assert(safe_get({a = 1}, "a") == 1) assert(safe_get({a = 1}, "b", 0) == 0) assert(safe_get(null, "a", 0) == 0) // Null-safe in conditional let maybe_obj = null if (maybe_obj?.value != null) { assert(false) // should not reach } let real_obj = {value = 42} if (real_obj?.value != null) { assert(real_obj.value == 42) } print("null_safety: OK\n") ================================================ FILE: testData/exec/valid_syntax/null_safety.out ================================================ null_safety: OK ================================================ FILE: testData/exec/valid_syntax/operators.nut ================================================ // Valid syntax: All operators // Arithmetic operators let a = 10 + 5 // 15 let b = 10 - 5 // 5 let c = 10 * 5 // 50 let d = 10 / 5 // 2 let e = 10 % 3 // 1 assert(a == 15) assert(b == 5) assert(c == 50) assert(d == 2) assert(e == 1) // Unary negation let neg = -42 assert(neg == -42) // Increment/decrement local inc_val = 5 inc_val++ assert(inc_val == 6) ++inc_val assert(inc_val == 7) inc_val-- assert(inc_val == 6) --inc_val assert(inc_val == 5) // Post-increment returns old value local pv = 10 let old_val = pv++ assert(old_val == 10) assert(pv == 11) // Pre-increment returns new value let new_val = ++pv assert(new_val == 12) assert(pv == 12) // Comparison operators assert(5 == 5) assert(5 != 6) assert(5 < 6) assert(6 > 5) assert(5 <= 5) assert(5 <= 6) assert(5 >= 5) assert(6 >= 5) // Three-way comparison assert((5 <=> 6) < 0) assert((5 <=> 5) == 0) assert((6 <=> 5) > 0) // Logical operators assert(true && true) assert(!(true && false)) assert(true || false) assert(!(false || false)) assert(!false) assert(!!true) // Bitwise operators assert((0xFF & 0x0F) == 0x0F) assert((0xF0 | 0x0F) == 0xFF) assert((0xFF ^ 0x0F) == 0xF0) assert(~0 == -1) assert((1 << 4) == 16) assert((16 >> 2) == 4) assert((16 >>> 2) == 4) // Compound assignment local ca = 10 ca += 5 assert(ca == 15) ca -= 3 assert(ca == 12) ca *= 2 assert(ca == 24) ca /= 4 assert(ca == 6) ca %= 4 assert(ca == 2) // Ternary operator let ter = true ? "yes" : "no" assert(ter == "yes") let ter2 = false ? "yes" : "no" assert(ter2 == "no") // typeof operator assert(typeof 42 == "integer") assert(typeof 3.14 == "float") assert(typeof "str" == "string") assert(typeof true == "bool") assert(typeof null == "null") assert(typeof [] == "array") assert(typeof {} == "table") // in operator let tbl = {a = 1, b = 2} assert("a" in tbl) assert(!("c" in tbl)) // not in operator assert("c" not in tbl) assert(!("a" not in tbl)) // instanceof class MyClass {} let inst = MyClass() assert(inst instanceof MyClass) // Null-coalescing operator let nc1 = null ?? "default" assert(nc1 == "default") let nc2 = "value" ?? "default" assert(nc2 == "value") let nc3 = null ?? null ?? "last" assert(nc3 == "last") // String concatenation with + let str_cat = "hello" + " " + "world" assert(str_cat == "hello world") // delete operator (with allow directive) #allow-delete-operator local del_tbl = {a = 1, b = 2} delete del_tbl.a assert(!("a" in del_tbl)) // clone operator let orig = {x = 1, y = 2} let copy = clone orig assert(copy.x == 1) copy.x = 99 assert(orig.x == 1) print("operators: OK\n") ================================================ FILE: testData/exec/valid_syntax/operators.out ================================================ operators: OK ================================================ FILE: testData/exec/valid_syntax/static_memo.nut ================================================ // Valid syntax: Static memoization // Static with simple value function get_config() { return static { name = "app", version = "1.0" } } let cfg1 = get_config() let cfg2 = get_config() // Both return the same cached frozen object assert(cfg1.name == "app") assert(cfg1.version == "1.0") // Static with array function get_list() { return static [1, 2, 3, 4, 5] } let list1 = get_list() assert(list1.len() == 5) assert(list1[0] == 1) // Static with simple expression function get_value() { return static 42 * 2 + 1 } assert(get_value() == 85) // Static objects are frozen let frozen_tbl = static {a = 1, b = 2} assert(frozen_tbl.is_frozen()) let frozen_arr = static [1, 2, 3] assert(frozen_arr.is_frozen()) // Inline const expressions let const_arr = const [10, 20, 30] assert(const_arr.is_frozen()) assert(const_arr[0] == 10) let const_tbl = const {x = 1, y = 2} assert(const_tbl.is_frozen()) // Const function let const_fn = const @(x) x * 2 assert(const_fn(5) == 10) // freeze function let mutable_arr = [1, 2, 3] let immutable_arr = freeze(mutable_arr) assert(immutable_arr.is_frozen()) let mutable_tbl = {a = 1} let immutable_tbl = freeze(mutable_tbl) assert(immutable_tbl.is_frozen()) print("static_memo: OK\n") ================================================ FILE: testData/exec/valid_syntax/static_memo.out ================================================ static_memo: OK ================================================ FILE: testData/exec/valid_syntax/strings.nut ================================================ // Valid syntax: Strings // Basic strings let s1 = "hello" let s2 = "world" assert(s1 == "hello") // Escape sequences let esc_tab = "a\tb" let esc_nl = "a\nb" let esc_cr = "a\rb" let esc_bs = "a\\b" let esc_dq = "a\"b" let esc_sq = "a\'b" let esc_null = "a\0b" let esc_hex = "\x41\x42\x43" assert(esc_hex == "ABC") let esc_uni = "\u0048\u0069" assert(esc_uni == "Hi") // Verbatim strings let verb1 = @"no escapes \n \t here" assert(verb1.contains("\\n")) let verb2 = @"multiline verbatim" assert(verb2.contains("\n")) let verb3 = @"doubled ""quotes"" inside" assert(verb3.contains("\"")) // String interpolation let name = "World" let interp = $"Hello {name}!" assert(interp == "Hello World!") local val = 42 let interp2 = $"val={val}, doubled={val * 2}" assert(interp2 == "val=42, doubled=84") // String methods assert("hello".len() == 5) assert("hello world".indexof("world") == 6) assert("hello world".contains("world")) assert(!"hello".contains("xyz")) assert("HELLO".tolower() == "hello") assert("hello".toupper() == "HELLO") assert("hello world".slice(0, 5) == "hello") assert("hello world".slice(6) == "world") // split let parts = "a,b,c".split(",") assert(parts.len() == 3) assert(parts[0] == "a") assert(parts[2] == "c") // split_by_chars let chars_split = "a.b-c_d".split_by_chars(".-_") assert(chars_split.len() == 4) // strip / lstrip / rstrip assert(" hello ".strip() == "hello") assert(" hello ".lstrip() == "hello ") assert(" hello ".rstrip() == " hello") // startswith assert("hello world".startswith("hello")) assert(!"hello world".startswith("world")) // replace let replaced = "aabbcc".replace("bb", "XX") assert(replaced == "aaXXcc") // join let joined = ", ".join(["a", "b", "c"]) assert(joined == "a, b, c") // tointeger / tofloat assert("42".tointeger() == 42) assert("3.14".tofloat() == 3.14) assert("0xFF".tointeger(16) == 255) // String concatenation let concat = "hello" + " " + "world" assert(concat == "hello world") // concat with numbers let numstr = "val=" + 42 assert(numstr == "val=42") // Empty string let empty_str = "" assert(empty_str.len() == 0) // String as iterable local count = 0 foreach (ch in "hello") { count++ } assert(count == 5) // hasindex assert("hello".hasindex(0)) assert("hello".hasindex(4)) assert(!"hello".hasindex(5)) // hash let h = "hello".hash() assert(typeof h == "integer") // tostring on various types (use variables to avoid parse issues) local int_val = 42 assert(int_val.tostring() == "42") local float_val = 3.14 assert(float_val.tostring() == "3.14") assert(true.tostring() == "true") assert(false.tostring() == "false") print("strings: OK\n") ================================================ FILE: testData/exec/valid_syntax/strings.out ================================================ strings: OK ================================================ FILE: testData/exec/valid_syntax/tables.nut ================================================ // Valid syntax: Tables #allow-delete-operator // Empty table let empty = {} assert(empty.len() == 0) // Table with identifier keys let t1 = { name = "John", age = 30, active = true } assert(t1.name == "John") assert(t1.age == 30) assert(t1.active == true) // Table with computed/bracket keys let t2 = { ["dynamic-key"] = "value", [42] = "numeric key", ["a" + "b"] = "computed" } assert(t2["dynamic-key"] == "value") assert(t2[42] == "numeric key") assert(t2["ab"] == "computed") // Table with methods let t3 = { value = 10, function get() { return this.value }, function set(v) { this.value = v } } assert(t3.get() == 10) t3.set(20) assert(t3.get() == 20) // ES2015 shorthand local x = 123 local y = 456 let shorthand = {x, y} assert(shorthand.x == 123) assert(shorthand.y == 456) // Mixed shorthand and explicit local z = 789 let mixed = {x, y = 20, z} assert(mixed.x == 123) assert(mixed.y == 20) assert(mixed.z == 789) // Trailing comma let trailing = { a = 1, b = 2, } assert(trailing.a == 1) // No comma (space-separated) let no_comma = { a = 1 b = 2 c = 3 } assert(no_comma.len() == 3) // Nested table let nested = { inner = { deep = { value = 42 } } } assert(nested.inner.deep.value == 42) // Slot creation with <- local t4 = {} t4.newkey <- "hello" assert(t4.newkey == "hello") t4["another"] <- 42 assert(t4.another == 42) // Slot deletion local t5 = {a = 1, b = 2, c = 3} delete t5.a assert(!("a" in t5)) assert(t5.len() == 2) // Table methods let methods_tbl = {a = 1, b = 2, c = 3} assert(methods_tbl.len() == 3) assert(methods_tbl.rawin("a")) assert(methods_tbl.rawget("a") == 1) let keys = methods_tbl.keys() assert(keys.len() == 3) let values = methods_tbl.values() assert(values.len() == 3) // Table functional methods let mapped = methods_tbl.map(@(v) v * 2) assert(mapped.a == 2) let filtered = methods_tbl.filter(@(v) v > 1) assert(!("a" in filtered)) assert("b" in filtered) local each_sum = 0 methods_tbl.each(@(v) each_sum += v) assert(each_sum == 6) // rawset returns the table (for chaining) let chain_tbl = {} chain_tbl.rawset("a", 1).rawset("b", 2).rawset("c", 3) assert(chain_tbl.a == 1) assert(chain_tbl.b == 2) assert(chain_tbl.c == 3) // topairs let pairs = {x = 10, y = 20}.topairs() assert(pairs.len() == 2) // __merge let base_tbl = {a = 1, b = 2} let extra = {b = 3, c = 4} let merged = base_tbl.__merge(extra) assert(merged.a == 1) assert(merged.b == 3) assert(merged.c == 4) assert(base_tbl.b == 2) // original unchanged // __update (in-place) local updatable = {a = 1, b = 2} updatable.__update({b = 3, c = 4}) assert(updatable.b == 3) assert(updatable.c == 4) // in / not in let membership = {a = 1, b = 2} assert("a" in membership) assert("c" not in membership) // Iteration local iter_count = 0 foreach (k, v in {a = 1, b = 2, c = 3}) { iter_count++ } assert(iter_count == 3) // clone table let orig = {x = 1, y = [1, 2]} let cloned = clone orig cloned.x = 99 assert(orig.x == 1) // original not modified // Table with function values let fn_tbl = { add = @(a, b) a + b, mul = @(a, b) a * b, } assert(fn_tbl.add(2, 3) == 5) assert(fn_tbl.mul(2, 3) == 6) print("tables: OK\n") ================================================ FILE: testData/exec/valid_syntax/tables.out ================================================ tables: OK ================================================ FILE: testData/exec/valid_syntax/threads.nut ================================================ // Valid syntax: Threads/Coroutines // Create thread function thread_fn(a, b) { local val = suspend(a + b) return val * 2 } let t = newthread(thread_fn) assert(typeof t == "thread") // Start thread let first = t.call(3, 4) assert(first == 7) // a + b = 7 // Resume thread let result = t.wakeup(10) assert(result == 20) // 10 * 2 // Thread status function status_fn() { suspend("first") suspend("second") return "done" } let st = newthread(status_fn) assert(st.getstatus() == "idle") let v1 = st.call() assert(v1 == "first") assert(st.getstatus() == "suspended") let v2 = st.wakeup() assert(v2 == "second") assert(st.getstatus() == "suspended") let v3 = st.wakeup() assert(v3 == "done") // Multiple coroutines function counter(start) { local n = start while (true) { suspend(n) n++ } } let c1 = newthread(counter) let c2 = newthread(counter) assert(c1.call(0) == 0) assert(c2.call(100) == 100) assert(c1.wakeup() == 1) assert(c2.wakeup() == 101) assert(c1.wakeup() == 2) assert(c2.wakeup() == 102) // Passing values to suspended thread function receiver() { local val = suspend("ready") return val } let recv = newthread(receiver) let ready = recv.call() assert(ready == "ready") let final_val = recv.wakeup(42) assert(final_val == 42) print("threads: OK\n") ================================================ FILE: testData/exec/valid_syntax/threads.out ================================================ threads: OK ================================================ FILE: testData/exec/valid_syntax/type_annotations.nut ================================================ // Valid syntax: Type annotations // Variable type annotations local xi: int = 42 local xf: float = 3.14 local xs: string = "hello" local xb: bool = true local xn: null = null local xa: array = [1, 2] local xt: table = {a = 1} local xfn: function = @(x) x // let with type annotations let li: int = 100 let lf: float = 2.71 let ls: string = "world" // Union types local nu: int|null = null nu = 42 local ns: string|null = "text" ns = null local nif: int|float = 42 nif = 3.14 // Function parameter types function typed_add(a: int, b: int): int { return a + b } assert(typed_add(2, 3) == 5) // Function with union param types function flex(val: int|string) { return val } assert(flex(42) == 42) assert(flex("hi") == "hi") // Function with nullable params function opt(x: int|null = null): int { return x ?? 0 } assert(opt() == 0) assert(opt(10) == 10) // Lambda with type annotations let typed_lambda = @(x: int, y: int): int x + y assert(typed_lambda(3, 4) == 7) // number type (int | float) function accept_number(n: number): number { return n } assert(accept_number(42) == 42) assert(accept_number(3.14) == 3.14) // any type function accept_any(v: any): any { return v } assert(accept_any(42) == 42) assert(accept_any("str") == "str") assert(accept_any(null) == null) // Varargs type annotation function sum_floats(first: float, ...: float): float { local total = first foreach (v in vargv) total += v return total } assert(sum_floats(1.0, 2.0, 3.0) == 6.0) // Complex return type function maybe_string(flag: bool): string|null { if (flag) return "yes" return null } assert(maybe_string(true) == "yes") assert(maybe_string(false) == null) print("type_annotations: OK\n") ================================================ FILE: testData/exec/valid_syntax/type_annotations.out ================================================ type_annotations: OK ================================================ FILE: testData/exec/valid_syntax/variables.nut ================================================ // Valid syntax: Variable declarations and scoping // local declaration local x = 10 local y assert(x == 10) assert(y == null) // Multiple local declarations local a = 1, b = 2, c = 3 assert(a == 1) assert(b == 2) assert(c == 3) // let declarations (immutable bindings) let lx = 42 let ly = "hello" assert(lx == 42) assert(ly == "hello") // let with mutable objects let arr = [1, 2, 3] arr[0] = 99 assert(arr[0] == 99) let tbl = {a = 1} tbl.a = 2 assert(tbl.a == 2) // Block scoping { local inner = 100 assert(inner == 100) } // local in for loop for (local i = 0; i < 3; i++) { assert(i >= 0) } // Multiple assignment local m1 = 1 local m2 = 2 m1 = 10 m2 = 20 assert(m1 == 10) assert(m2 == 20) // Variable referencing earlier variable local va = 5 local vb = va + 1 assert(vb == 6) // Uninitialized local defaults to null local uninit assert(uninit == null) // Nested scopes with different variable names local outer = "outer" { local inner2 = "inner" assert(inner2 == "inner") assert(outer == "outer") } assert(outer == "outer") print("variables: OK\n") ================================================ FILE: testData/exec/valid_syntax/variables.out ================================================ variables: OK ================================================ FILE: testData/exec/weird/2000_args.nut ================================================ local function fn( a000, a001, a002, a003, a004, a005, a006, a007, a008, a009, a010, a011, a012, a013, a014, a015, a016, a017, a018, a019, a020, a021, a022, a023, a024, a025, a026, a027, a028, a029, a030, a031, a032, a033, a034, a035, a036, a037, a038, a039, a040, a041, a042, a043, a044, a045, a046, a047, a048, a049, a050, a051, a052, a053, a054, a055, a056, a057, a058, a059, a060, a061, a062, a063, a064, a065, a066, a067, a068, a069, a070, a071, a072, a073, a074, a075, a076, a077, a078, a079, a080, a081, a082, a083, a084, a085, a086, a087, a088, a089, a090, a091, a092, a093, a094, a095, a096, a097, a098, a099, a100, a101, a102, a103, a104, a105, a106, a107, a108, a109, a110, a111, a112, a113, a114, a115, a116, a117, a118, a119, a120, a121, a122, a123, a124, a125, a126, a127, a128, a129, a130, a131, a132, a133, a134, a135, a136, a137, a138, a139, a140, a141, a142, a143, a144, a145, a146, a147, a148, a149, a150, a151, a152, a153, a154, a155, a156, a157, a158, a159, a160, a161, a162, a163, a164, a165, a166, a167, a168, a169, a170, a171, a172, a173, a174, a175, a176, a177, a178, a179, a180, a181, a182, a183, a184, a185, a186, a187, a188, a189, a190, a191, a192, a193, a194, a195, a196, a197, a198, a199, a200, a201, a202, a203, a204, a205, a206, a207, a208, a209, a210, a211, a212, a213, a214, a215, a216, a217, a218, a219, a220, a221, a222, a223, a224, a225, a226, a227, a228, a229, a230, a231, a232, a233, a234, a235, a236, a237, a238, a239, a240, a241, a242, a243, a244, a245, a246, a247, a248, a249, a250, a251, a252, a253, a254, a255, a256, a257, a258, a259, a260, a261, a262, a263, a264, a265, a266, a267, a268, a269, a270, a271, a272, a273, a274, a275, a276, a277, a278, a279, a280, a281, a282, a283, a284, a285, a286, a287, a288, a289, a290, a291, a292, a293, a294, a295, a296, a297, a298, a299, a300, a301, a302, a303, a304, a305, a306, a307, a308, a309, a310, a311, a312, a313, a314, a315, a316, a317, a318, a319, a320, a321, a322, a323, a324, a325, a326, a327, a328, a329, a330, a331, a332, a333, a334, a335, a336, a337, a338, a339, a340, a341, a342, a343, a344, a345, a346, a347, a348, a349, a350, a351, a352, a353, a354, a355, a356, a357, a358, a359, a360, a361, a362, a363, a364, a365, a366, a367, a368, a369, a370, a371, a372, a373, a374, a375, a376, a377, a378, a379, a380, a381, a382, a383, a384, a385, a386, a387, a388, a389, a390, a391, a392, a393, a394, a395, a396, a397, a398, a399, a400, a401, a402, a403, a404, a405, a406, a407, a408, a409, a410, a411, a412, a413, a414, a415, a416, a417, a418, a419, a420, a421, a422, a423, a424, a425, a426, a427, a428, a429, a430, a431, a432, a433, a434, a435, a436, a437, a438, a439, a440, a441, a442, a443, a444, a445, a446, a447, a448, a449, a450, a451, a452, a453, a454, a455, a456, a457, a458, a459, a460, a461, a462, a463, a464, a465, a466, a467, a468, a469, a470, a471, a472, a473, a474, a475, a476, a477, a478, a479, a480, a481, a482, a483, a484, a485, a486, a487, a488, a489, a490, a491, a492, a493, a494, a495, a496, a497, a498, a499, a500, a501, a502, a503, a504, a505, a506, a507, a508, a509, a510, a511, a512, a513, a514, a515, a516, a517, a518, a519, a520, a521, a522, a523, a524, a525, a526, a527, a528, a529, a530, a531, a532, a533, a534, a535, a536, a537, a538, a539, a540, a541, a542, a543, a544, a545, a546, a547, a548, a549, a550, a551, a552, a553, a554, a555, a556, a557, a558, a559, a560, a561, a562, a563, a564, a565, a566, a567, a568, a569, a570, a571, a572, a573, a574, a575, a576, a577, a578, a579, a580, a581, a582, a583, a584, a585, a586, a587, a588, a589, a590, a591, a592, a593, a594, a595, a596, a597, a598, a599, a600, a601, a602, a603, a604, a605, a606, a607, a608, a609, a610, a611, a612, a613, a614, a615, a616, a617, a618, a619, a620, a621, a622, a623, a624, a625, a626, a627, a628, a629, a630, a631, a632, a633, a634, a635, a636, a637, a638, a639, a640, a641, a642, a643, a644, a645, a646, a647, a648, a649, a650, a651, a652, a653, a654, a655, a656, a657, a658, a659, a660, a661, a662, a663, a664, a665, a666, a667, a668, a669, a670, a671, a672, a673, a674, a675, a676, a677, a678, a679, a680, a681, a682, a683, a684, a685, a686, a687, a688, a689, a690, a691, a692, a693, a694, a695, a696, a697, a698, a699, a700, a701, a702, a703, a704, a705, a706, a707, a708, a709, a710, a711, a712, a713, a714, a715, a716, a717, a718, a719, a720, a721, a722, a723, a724, a725, a726, a727, a728, a729, a730, a731, a732, a733, a734, a735, a736, a737, a738, a739, a740, a741, a742, a743, a744, a745, a746, a747, a748, a749, a750, a751, a752, a753, a754, a755, a756, a757, a758, a759, a760, a761, a762, a763, a764, a765, a766, a767, a768, a769, a770, a771, a772, a773, a774, a775, a776, a777, a778, a779, a780, a781, a782, a783, a784, a785, a786, a787, a788, a789, a790, a791, a792, a793, a794, a795, a796, a797, a798, a799, a800, a801, a802, a803, a804, a805, a806, a807, a808, a809, a810, a811, a812, a813, a814, a815, a816, a817, a818, a819, a820, a821, a822, a823, a824, a825, a826, a827, a828, a829, a830, a831, a832, a833, a834, a835, a836, a837, a838, a839, a840, a841, a842, a843, a844, a845, a846, a847, a848, a849, a850, a851, a852, a853, a854, a855, a856, a857, a858, a859, a860, a861, a862, a863, a864, a865, a866, a867, a868, a869, a870, a871, a872, a873, a874, a875, a876, a877, a878, a879, a880, a881, a882, a883, a884, a885, a886, a887, a888, a889, a890, a891, a892, a893, a894, a895, a896, a897, a898, a899, a900, a901, a902, a903, a904, a905, a906, a907, a908, a909, a910, a911, a912, a913, a914, a915, a916, a917, a918, a919, a920, a921, a922, a923, a924, a925, a926, a927, a928, a929, a930, a931, a932, a933, a934, a935, a936, a937, a938, a939, a940, a941, a942, a943, a944, a945, a946, a947, a948, a949, a950, a951, a952, a953, a954, a955, a956, a957, a958, a959, a960, a961, a962, a963, a964, a965, a966, a967, a968, a969, a970, a971, a972, a973, a974, a975, a976, a977, a978, a979, a980, a981, a982, a983, a984, a985, a986, a987, a988, a989, a990, a991, a992, a993, a994, a995, a996, a997, a998, a999, b000, b001, b002, b003, b004, b005, b006, b007, b008, b009, b010, b011, b012, b013, b014, b015, b016, b017, b018, b019, b020, b021, b022, b023, b024, b025, b026, b027, b028, b029, b030, b031, b032, b033, b034, b035, b036, b037, b038, b039, b040, b041, b042, b043, b044, b045, b046, b047, b048, b049, b050, b051, b052, b053, b054, b055, b056, b057, b058, b059, b060, b061, b062, b063, b064, b065, b066, b067, b068, b069, b070, b071, b072, b073, b074, b075, b076, b077, b078, b079, b080, b081, b082, b083, b084, b085, b086, b087, b088, b089, b090, b091, b092, b093, b094, b095, b096, b097, b098, b099, b100, b101, b102, b103, b104, b105, b106, b107, b108, b109, b110, b111, b112, b113, b114, b115, b116, b117, b118, b119, b120, b121, b122, b123, b124, b125, b126, b127, b128, b129, b130, b131, b132, b133, b134, b135, b136, b137, b138, b139, b140, b141, b142, b143, b144, b145, b146, b147, b148, b149, b150, b151, b152, b153, b154, b155, b156, b157, b158, b159, b160, b161, b162, b163, b164, b165, b166, b167, b168, b169, b170, b171, b172, b173, b174, b175, b176, b177, b178, b179, b180, b181, b182, b183, b184, b185, b186, b187, b188, b189, b190, b191, b192, b193, b194, b195, b196, b197, b198, b199, b200, b201, b202, b203, b204, b205, b206, b207, b208, b209, b210, b211, b212, b213, b214, b215, b216, b217, b218, b219, b220, b221, b222, b223, b224, b225, b226, b227, b228, b229, b230, b231, b232, b233, b234, b235, b236, b237, b238, b239, b240, b241, b242, b243, b244, b245, b246, b247, b248, b249, b250, b251, b252, b253, b254, b255, b256, b257, b258, b259, b260, b261, b262, b263, b264, b265, b266, b267, b268, b269, b270, b271, b272, b273, b274, b275, b276, b277, b278, b279, b280, b281, b282, b283, b284, b285, b286, b287, b288, b289, b290, b291, b292, b293, b294, b295, b296, b297, b298, b299, b300, b301, b302, b303, b304, b305, b306, b307, b308, b309, b310, b311, b312, b313, b314, b315, b316, b317, b318, b319, b320, b321, b322, b323, b324, b325, b326, b327, b328, b329, b330, b331, b332, b333, b334, b335, b336, b337, b338, b339, b340, b341, b342, b343, b344, b345, b346, b347, b348, b349, b350, b351, b352, b353, b354, b355, b356, b357, b358, b359, b360, b361, b362, b363, b364, b365, b366, b367, b368, b369, b370, b371, b372, b373, b374, b375, b376, b377, b378, b379, b380, b381, b382, b383, b384, b385, b386, b387, b388, b389, b390, b391, b392, b393, b394, b395, b396, b397, b398, b399, b400, b401, b402, b403, b404, b405, b406, b407, b408, b409, b410, b411, b412, b413, b414, b415, b416, b417, b418, b419, b420, b421, b422, b423, b424, b425, b426, b427, b428, b429, b430, b431, b432, b433, b434, b435, b436, b437, b438, b439, b440, b441, b442, b443, b444, b445, b446, b447, b448, b449, b450, b451, b452, b453, b454, b455, b456, b457, b458, b459, b460, b461, b462, b463, b464, b465, b466, b467, b468, b469, b470, b471, b472, b473, b474, b475, b476, b477, b478, b479, b480, b481, b482, b483, b484, b485, b486, b487, b488, b489, b490, b491, b492, b493, b494, b495, b496, b497, b498, b499, b500, b501, b502, b503, b504, b505, b506, b507, b508, b509, b510, b511, b512, b513, b514, b515, b516, b517, b518, b519, b520, b521, b522, b523, b524, b525, b526, b527, b528, b529, b530, b531, b532, b533, b534, b535, b536, b537, b538, b539, b540, b541, b542, b543, b544, b545, b546, b547, b548, b549, b550, b551, b552, b553, b554, b555, b556, b557, b558, b559, b560, b561, b562, b563, b564, b565, b566, b567, b568, b569, b570, b571, b572, b573, b574, b575, b576, b577, b578, b579, b580, b581, b582, b583, b584, b585, b586, b587, b588, b589, b590, b591, b592, b593, b594, b595, b596, b597, b598, b599, b600, b601, b602, b603, b604, b605, b606, b607, b608, b609, b610, b611, b612, b613, b614, b615, b616, b617, b618, b619, b620, b621, b622, b623, b624, b625, b626, b627, b628, b629, b630, b631, b632, b633, b634, b635, b636, b637, b638, b639, b640, b641, b642, b643, b644, b645, b646, b647, b648, b649, b650, b651, b652, b653, b654, b655, b656, b657, b658, b659, b660, b661, b662, b663, b664, b665, b666, b667, b668, b669, b670, b671, b672, b673, b674, b675, b676, b677, b678, b679, b680, b681, b682, b683, b684, b685, b686, b687, b688, b689, b690, b691, b692, b693, b694, b695, b696, b697, b698, b699, b700, b701, b702, b703, b704, b705, b706, b707, b708, b709, b710, b711, b712, b713, b714, b715, b716, b717, b718, b719, b720, b721, b722, b723, b724, b725, b726, b727, b728, b729, b730, b731, b732, b733, b734, b735, b736, b737, b738, b739, b740, b741, b742, b743, b744, b745, b746, b747, b748, b749, b750, b751, b752, b753, b754, b755, b756, b757, b758, b759, b760, b761, b762, b763, b764, b765, b766, b767, b768, b769, b770, b771, b772, b773, b774, b775, b776, b777, b778, b779, b780, b781, b782, b783, b784, b785, b786, b787, b788, b789, b790, b791, b792, b793, b794, b795, b796, b797, b798, b799, b800, b801, b802, b803, b804, b805, b806, b807, b808, b809, b810, b811, b812, b813, b814, b815, b816, b817, b818, b819, b820, b821, b822, b823, b824, b825, b826, b827, b828, b829, b830, b831, b832, b833, b834, b835, b836, b837, b838, b839, b840, b841, b842, b843, b844, b845, b846, b847, b848, b849, b850, b851, b852, b853, b854, b855, b856, b857, b858, b859, b860, b861, b862, b863, b864, b865, b866, b867, b868, b869, b870, b871, b872, b873, b874, b875, b876, b877, b878, b879, b880, b881, b882, b883, b884, b885, b886, b887, b888, b889, b890, b891, b892, b893, b894, b895, b896, b897, b898, b899, b900, b901, b902, b903, b904, b905, b906, b907, b908, b909, b910, b911, b912, b913, b914, b915, b916, b917, b918, b919, b920, b921, b922, b923, b924, b925, b926, b927, b928, b929, b930, b931, b932, b933, b934, b935, b936, b937, b938, b939, b940, b941, b942, b943, b944, b945, b946, b947, b948, b949, b950, b951, b952, b953, b954, b955, b956, b957, b958, b959, b960, b961, b962, b963, b964, b965, b966, b967, b968, b969, b970, b971, b972, b973, b974, b975, b976, b977, b978, b979, b980, b981, b982, b983, b984, b985, b986, b987, b988, b989, b990, b991, b992, b993, b994, b995, b996, b997, b998, b999 ) {} ================================================ FILE: testData/exec/weird/2000_args.out ================================================ ================================================ FILE: testData/exec/weird/assign_to_optional_1.nut ================================================ local t = {a = 5} t?.a = 10 ================================================ FILE: testData/exec/weird/assign_to_optional_1.out ================================================ ================================================ FILE: testData/exec/weird/assign_to_optional_2.nut ================================================ local t = {a = 5} t?.b <- 10 ================================================ FILE: testData/exec/weird/assign_to_optional_2.out ================================================ ================================================ FILE: testData/exec/weird/assign_to_optional_3.nut ================================================ local t = {a = 5} t?["c"] <- 20 ================================================ FILE: testData/exec/weird/assign_to_optional_3.out ================================================ ================================================ FILE: testData/exec/weird/many_locals.nut ================================================ #disable-optimizer #allow-switch-statement let _sv = @(_e) (type(_e) == "integer" || type(_e) == "float" || type(_e) == "null" || type(_e) == "bool" || type(_e) == "string") ? "" + _e : type(_e) try { // Code Bricks assembly - seed 1680 global enum S1680_E1 { A, B, C } let v0 = S1680_E1.A + S1680_E1.B + S1680_E1.C // [enum_basic] let v1_fn = function(x: int|float): int { return x.tointeger() } let v1 = v1_fn(v0) // [typed_union_param] let v2_fn = function(x: int|null): int { return x != null ? x : -255 } let v2 = v2_fn(v0) // [typed_nullable_param] let v3_fn = function(p0, p1) { local u0 = 3 switch (v2 & 3) { case 0: u0 = -65538; break case 1: u0 = '9'; break case 2: u0 = -4; break default: u0 = 128 } // [switch_int] local u1_t = {arr = [2, null, {key = p1}]} local u1_inner = u1_t?.arr?[2] if (u1_inner != null) u1_inner.key += 9223372036854775807 let u1 = u1_t?.arr?[2]?.key ?? 129 // [nested_table_array_modify] let u2 = [v1, p1] // [array_of_ints] let u3 = [p1, v1] // [array_of_ints] let {da = 0xFFFFFFFF, db = 127} = {da = u0} let u4 = da + db // [destruct_defaults] let u5_fn = function([da, db]) { return da - db } let u5 = u5_fn([v1, u4]) // [destruct_param_array] let u6_Base = class { function compute(x) { return x + 0xFF } } let u6_Der = class(u6_Base) { function compute(x) { return base.compute(x) * -32770 } } let u6 = u6_Der().compute(u0) // [class_method_override] let u7 = $"val={u1}" // [string_interp] return u7 } let v3 = v3_fn(v1, v0) // [nested_func depth=1] let v4_fn = function() { local u0 = v1 u0++ // [postfix_inc] let u1 = static({x = -2147483649, y = -32768}) // [static_table] let u2 = v3.len() > 2 ? v3.slice(-2) : v3 // [string_slice_neg] const u3 = -1 // [const_int] local u4_val = 0 let u4_fn = function(x) { u4_val = u4_val + x } local u4_i = 0 while (u4_i < 2) { u4_fn(v0) u4_i++ } let u4 = u4_val // [closure_in_loop] let u5 = (function() { let g = (function() { for (local i = 0; i < 0; i++) yield i })() local s = 0 while (g.getstatus() == "suspended") { s += resume g } return s })() // [coroutine_sum] return u5 } let v4 = v4_fn() // [nested_func depth=1] let v5_fn = function(...) { local m = 257 foreach (v in vargv) if (typeof v == "integer" && v > m) m = v return m } let v5 = v5_fn(v2, v0) // [vargv_max] local v6 = v1 v6-- // [postfix_dec] let v7_fn = function(p0, p1, p2) { let {da = -65536, db = 0x8000} = {da = p2} let u0 = da + db // [destruct_defaults] let u1 = array(p1 & 7, -2147483647) // [array_function_init] enum S1680_E2 { LOW = 0, MID = 50, HIGH = 100 } local u2 = "" if (v4 < S1680_E2.MID) u2 = "low" else if (v4 < S1680_E2.HIGH) u2 = "mid" else u2 = "high" // [enum_compare_chain] local u3 = p1 u3-- // [postfix_dec] let u4_fn = function(q0, q1, q2) { let w0 = u1.filter(@(v) v != null).map(@(v) v) // [array_filter_map] let w1_fn = function(x, ops = [@(v) v + -129, @(v) v * 2147483648, @(v) v - 0x7FFFFFFF]) { local r = x foreach (op in ops) r = op(r) return r } let w1_a = w1_fn(v1) let w1_b = w1_fn(v1, [@(v) v, @(v) v + -32770]) let w1 = w1_a + w1_b // [default_param_array_of_fns] let w2 = w0.indexof(p2) ?? 2147483648 // [array_indexof] let w3_fn = function(x, opts = {f = @(v) v + -65538, scale = -256}) { return opts.f(x) * opts.scale } let w3_a = w3_fn(w1) let w3_b = w3_fn(w1, {f = @(v) v * -256, scale = -32769}) let w3 = w3_a + w3_b // [default_param_fn_in_table] let w4_fn = function(r0, r1) { let z0 = v3 != r1 // [string_cmp_ne] let z1 = u3 ^ v5 // [int_xor] let z2 = q1 > v4 // [int_cmp_gt] local z3 = 0 for (local z3_i = 8; z3_i > 0; z3_i--) { z3 += p0 } // [for_countdown] return z3 } let w4 = w4_fn(v0, u2) // [nested_func depth=3] return w4 } let u4 = u4_fn(v6, p1, u2) // [nested_func depth=2] return u4 } let v7 = v7_fn(v4, v2, v0) // [nested_func depth=1] let v8_fn = function(p0, p1, p2) { let u0 = p1 assert(u0 >= 0 || u0 <= 0, "value check") // [assert_msg] let u1_t = {x = v7} u1_t.x -= 65537 let u1 = u1_t.x // [table_field_minuseq] global enum S1680_E3 { A, B, C } let u2 = S1680_E3.A + S1680_E3.B + S1680_E3.C // [enum_basic] let u3 = (((false ? ' ' : v7)).tofloat() != (((-129) >>> 1)).tofloat()) // [bool_literal] let u4_outer = function(x) { let inner = @(n) n * -256 return inner(x) + 0x8000 } let u4 = u4_outer(u1) // [nested_function] local u5: int = p2 // [typed_int_var] return u5 } let v8 = v8_fn(v6, v2, v7) // [nested_func depth=1] let v9_C = class { xv = 0 constructor(x) { this.xv = x } } let v9_arr = [v9_C(v5), v9_C(v5 + 1), v9_C(v5 + 2)] let v9 = v9_arr[0].xv + v9_arr[1].xv + v9_arr[2].xv // [class_array_of] global enum S1680_E4 { A = "\u0100", B = "-1" } let v10 = $"{S1680_E4.A}{S1680_E4.B}" // [enum_string] let v11_fn = function(p0, p1, p2) { let u0_a = [v9, v9 + 1] u0_a.append(v9 * 2) let u0 = u0_a.pop() // [array_push_pop] local u1 = v5 u1++ // [postfix_inc] let u2 = array(v6 & 7, 0x7FFFFFFF) // [array_function_init] let u3 = v6 + p1 // [int_add] let u4 = u2.len() > 0 ? u2.reduce(@(a, _b) a) : 0x1 // [array_reduce_lambda] local u5_rows = [{v = v9}, null, {v = v9 + 1}, null, {v = '\\'}] local u5 = 0 foreach (row in u5_rows) u5 += row?.v ?? 0 // [array_of_tables_nullable_sum] let [u6_a, u6_b] = [u4, u4 + 1] let [u6_c] = [u6_a + u6_b] let u6 = u6_c // [destruct_nested] return u6 } let v11 = v11_fn(v0, v2, v7) // [nested_func depth=1] let v12_fn = function(p0, p1, p2) { let u0 = v9 + v7 // [int_add] local u1 = p2 u1 /= p0 // [modify_div_int_unsafe] let u2_fn = function(q0, q1) { let w0 = v10.split("\u0001") // [string_split] let w1_base = {extra = 256, x = -256} let w1_t = {x = v4} let w1 = w1_t.rawget("x") ?? w1_base.extra // [table_setdelegate] let w2 = u1 != 0 ? w1 % u1 : 65537 // [int_mod_safe] local w3_ops = { inc = @(x) x + -130, dec = @(x) x - 257, dbl = @(x) x * 2 } let w3_op = w3_ops?[v10] let w3 = w3_op != null ? w3_op(v9) : v9 // [table_of_closures_dispatch] local w4 = v9 w4 /= w2 // [modify_div_int_unsafe] return w4 } let u2 = u2_fn(v2, v3) // [nested_func depth=2] let u3_fn = function(q0, q1, q2) { let w0 = u0 | v7 // [int_bitor] let w1_Base = class { xv = 0; constructor(x) { this.xv = x } } let w1_Der = class(w1_Base) { constructor(x) { base.constructor(x) } } let w1_obj = w1_Der(q0) let w1 = w1_obj instanceof w1_Base // [class_instanceof_base] let w2_t = {x = w0} w2_t.x -= 9223372036854775807 let w2 = w2_t.x // [table_field_minuseq] let w3_C = class { xv = 0 constructor(x) { this.xv = x } function _unm() { return this.getclass()(-this.xv) } } let w3_r = -w3_C(v4) let w3 = w3_r.xv // [meta_unm] let w4 = v0 < p0 ? v0 : p0 // [int_min_two] return w4 } let u3 = u3_fn(v6, v3, u0) // [nested_func depth=2] let u4_a = [u0] u4_a[0] *= 2147483648 let u4 = u4_a[0] // [array_elem_muleq] let u5_fn = function(q0, q1) { local w0_val = v2 let w0_set = function(x) { w0_val = x } let w0_get = function() { return w0_val } w0_set(v2 + 1) let w0 = w0_get() // [closure_getter_setter] local w1 = 128 switch (v1 % 3) { case 0: w1 = -2147483649; break case 1: w1 = 65536; break default: w1 = 2147483647; break } // [switch_default] let w2_fn = function(r0, r1, r2) { let z0_Base = class { xv = 0; constructor(x) { this.xv = x } } let z0_Der = class(z0_Base) { constructor(x) { base.constructor(x) } } let z0_obj = z0_Der(w1) let z0 = z0_obj instanceof z0_Base // [class_instanceof_base] let z1_make = @(bv) @(x) x + bv let z1_add = z1_make(u2) let z1 = z1_add(-129) // [func_returns_func] let z2_fn = function(x, ops = [@(v) v + 4294967297, @(v) v * -4294967296, @(v) v - -3]) { local r = x foreach (op in ops) r = op(r) return r } let z2_a = z2_fn(z1) let z2_b = z2_fn(z1, [@(v) v, @(v) v + 2]) let z2 = z2_a + z2_b // [default_param_array_of_fns] return z2 } let w2 = w2_fn(v2, w1, v1) // [nested_func depth=3] return w2 } let u5 = u5_fn(v10, v4) // [nested_func depth=2] let u6_C = class { xv = 0 constructor(x) { this.xv = x } function _add(o) { return this.getclass()(this.xv + o.xv) } } let u6_r = u6_C(v5) + u6_C(u4) let u6 = u6_r.xv // [meta_add] local u7 = v7 ++u7 // [prefix_inc] return u7 } let v12 = v12_fn(v6, v3, v2) // [nested_func depth=1] local v13 = v2 ++v13 // [prefix_inc] let v14 = v3.startswith(v10) // [string_startswith] local v15_state = {scores = [v8, -2147483648, 4294967296], tag = "\u0001"} v15_state.scores[0] += 4294967295 v15_state.scores[2] = v15_state.scores[0] + v15_state.scores[1] let v15 = v15_state.scores.reduce(@(a, v) a + v, 0) // [table_slot_array_modify] let v16 = (function() { let g = (function() { for (local i = 0; i < 2; i++) yield i })() local s = 0 while (g.getstatus() == "suspended") { s += resume g } return s })() // [coroutine_sum] let v17 = v7 * v5 // [int_mul] let v18_fn = function() { const u0 = true // [const_bool] let u1 = v10.len() > 2 ? v10.slice(-2) : v10 // [string_slice_neg] local u2_sum = 0 let u2_add = function(x) { u2_sum = u2_sum + x } u2_add(v7) u2_add(v16) u2_add(v7 + v16) let u2 = u2_sum // [closure_accumulator] let u3_fns = [@(x) x + 32769, @(x) x * 0, @(x) x - -65536] local u3 = v15 foreach (f in u3_fns) u3 = f(u3) // [array_of_closures_accumulate] let u4 = -v9 // [int_neg] return u4 } let v18 = v18_fn() // [nested_func depth=1] let v19_fn = function(p0, p1) { local u0 = -129 try { u0 = v3.tointeger() } catch (_e) {} // [string_tointeger] let u1_fn = @[pure, nodiscard] (a, b) a + b let u1 = u1_fn(p1, v1) // [func_pure_nodiscard] let u2_fn = function(q0, q1, q2) { local w0_ops = { inc = @(x) x + -65538, dec = @(x) x - 2147483649, dbl = @(x) x * 2 } let w0_op = w0_ops?[q2] let w0 = w0_op != null ? w0_op(v11) : v11 // [table_of_closures_dispatch] let w1 = p0.replace(";", "\\") // [string_replace] let w2_make = @(bv) @(x) x + bv let w2_add = w2_make(u0) let w2 = w2_add(-65537) // [func_returns_func] local w3_store = {pages = [{lines = [0x8000, -32767]}, {lines = [-1, -9223372036854775807]}]} w3_store.pages[0].lines[1] += w0 let w3 = w3_store?.pages?[0]?.lines?[1] ?? -129 // [nested_db_array_field] let w4_t = freeze({x = v1, y = -32769}) let w4 = w4_t.x + w4_t.y // [freeze_nested] return w4 } let u2 = u2_fn(p1, v4, p0) // [nested_func depth=2] let u3 = v18 / v17 // [int_div_unsafe] let u4 = [p0, v3, " "] // [array_of_strings] let u5 = v3.tofloat() // [string_tofloat] local u6 = v6 u6 += u1 u6 -= u0 u6 *= -4 // [modify_chain] return u6 } let v19 = v19_fn(v3, v15) // [nested_func depth=1] let v20 = v15 > 0 ? 1 : (v15 < 0 ? -1 : 0) // [int_sign] let v21 = v10.len() > 4 ? v10.slice(1, 3) : v10 // [string_slice_both] let v22_fn = function(x, opts = {f = @(v) v + 65537, scale = 257}) { return opts.f(x) * opts.scale } let v22_a = v22_fn(v2) let v22_b = v22_fn(v2, {f = @(v) v * 4294967297, scale = -65535}) let v22 = v22_a + v22_b // [default_param_fn_in_table] let v23_fn = @(a: int, b: int) a * b let v23 = v23_fn(v16, v5) // [typed_lambda] let v24_fn = function() { local u0 = 0 for (local u0_i = 0; u0_i < -1; u0_i++) { u0 = u0 + v12 } // [for_sum] let u1 = typeof v3 == "string" // [typeof_string_check] let u2 = [v1, u0] // [array_of_ints] local u3 = u0 u3++ // [postfix_inc] let u4_fn = function(...) { local s = 0 foreach (v in vargv) s += v return s } let u4 = u4_fn(v16, v7, 2) // [vargv_sum] return u4 } let v24 = v24_fn() // [nested_func depth=1] let v25_fn = function(x, s, cfg = { fmt = @(n, t) $"{n}:{t}", limits = {lo = 0xFFFF, hi = -256} }) { local clamped = x < cfg.limits.lo ? cfg.limits.lo : x > cfg.limits.hi ? cfg.limits.hi : x return cfg.fmt(clamped, s) } let v25 = v25_fn(v16, v3) // [default_param_config_nested] let v26_fn = function(x: int|float): int { return x.tointeger() } let v26 = v26_fn(v12) // [typed_union_param] let v27_fn = function(p0, p1, p2) { local u0_ops = { inc = @(x) x + -65536, dec = @(x) x - 257, dbl = @(x) x * 2 } let u0_op = u0_ops?[v25] let u0 = u0_op != null ? u0_op(v11) : v11 // [table_of_closures_dispatch] let u1 = ((0xFF).tofloat()).tointeger() // [int_literal] let u2_C = class { xv = 0 constructor(x) { this.xv = x } function _cmp(o) { return this.xv <=> o.xv } } let u2 = u2_C(v13) < u2_C(p1) // [meta_cmp] let u3 = [-4294967295, -256, -2147483648] u3.insert(1, v6) u3.remove(0) // [array_insert_remove] let u4_fn = function({da, db = "\\\\"}) { return $"{_sv(da)}{_sv(db)}" } let u4 = u4_fn({da = v19, db = v21}) // [destruct_param_nested_table] return u4 } let v27 = v27_fn(v16, v20, v15) // [nested_func depth=1] let v28 = v19 >= 0 ? v19 : -v19 // [int_abs_ternary] let v29 = $"flag={v14}" // [string_interp_bool] let v30_C = class { _v = 0 constructor(xv) { this._v = xv } } let v30_obj = v30_C(v9) let v30 = v30_obj instanceof v30_C // [instanceof_check] enum S1680_E5 { A, B, C } let v31 = S1680_E5.A + S1680_E5.B + S1680_E5.C // [enum_basic] let v32_a = [v6, v22, -4294967297] let v32 = v32_a.top() // [array_top] local v33_val = v7 let v33_set = function(x) { v33_val = x } let v33_get = function() { return v33_val } v33_set(v7 + 0xCAFE) let v33 = v33_get() // [closure_getter_setter] let v34_fn = function([da, db = 0xFFFF]) { return da * db } let v34 = v34_fn([v19]) // [destruct_param_array_default] let v35 = v3.len() // [string_len] let v36_C = class { xv = 0 constructor(x) { this.xv = x } function _mul(o) { return this.getclass()(this.xv * o.xv) } } let v36_r = v36_C(v20) * v36_C(v6) let v36 = v36_r.xv // [meta_mul] let v37 = [v25, v29, ""] // [array_of_strings] let v38 = "{0}+{1}".subst(v29, "hello") // [string_subst] let v39_fn = function(p0, p1, p2) { let u0 = v25.startswith(v3) // [string_startswith] local u1 = v2 if (v20 != 0) u1 %= v20 // [modify_mod_int_safe] let u2_C = class { _n = 0 constructor(n) { this._n = n & 7 } function _nexti(idx) { local next = idx == null ? 0 : idx + 1 return next < this._n ? next : null } function _get(idx) { return idx * 255 } } let u2_obj = u2_C(v1) local u2 = 0 foreach (_i, v in u2_obj) u2 += v // [meta_nexti] let u3 = v36 << 3 // [int_shl] local u4 = 4294967297 foreach (v in v37) { if (typeof v == "integer") { u4 = v; break } } // [foreach_array_break] return u4 } let v39 = v39_fn(v18, v15, v25) // [nested_func depth=1] let v40_fn = function(x, cls = class { function calc(v) { return v + 128 } }) { return cls().calc(x) } let v40_a = v40_fn(v32) let v40_other = class { function calc(v) { return v * 9223372036854775807 } } let v40_b = v40_fn(v32, v40_other) let v40 = v40_a + v40_b // [default_param_class] let v41_Base = class { function compute(x) { return x + -127 } } let v41_Der = class(v41_Base) { function compute(x) { return base.compute(x) * 2 } } let v41 = v41_Der().compute(v23) // [class_method_override] local v42_ops = { inc = @(x) x + ' ', dec = @(x) x - 42, dbl = @(x) x * 2 } let v42_op = v42_ops?[v29] let v42 = v42_op != null ? v42_op(v1) : v1 // [table_of_closures_dispatch] let v43 = v34 + v5 // [int_add] let v44 = v14 && v30 // [bool_and] let v45 = null // [null_var] let v46_fn = function(p0, p1) { let u0 = v3.tolower() // [string_tolower] local u1 = "" switch (v31 <=> -128) { case -1: u1 = "less"; break case 0: u1 = "equal"; break case 1: u1 = "greater"; break default: u1 = "other" } // [switch_three_way] let u2_fn = function() { let w0_fn = function(x, pipe = {pre = @(v) v & 0xFF, run = @(v) v + 32767, post = @(v) v * -257}) { return pipe.post(pipe.run(pipe.pre(x))) } let w0 = w0_fn(p0) // [default_param_pipeline] let w1 = [v3, v21, "\t\n\r"] // [array_of_strings] let w2_fn = function(x, pipe = {pre = @(v) v & 0xFF, run = @(v) v + -257, post = @(v) v * 2}) { return pipe.post(pipe.run(pipe.pre(x))) } let w2 = w2_fn(v13) // [default_param_pipeline] let w3 = v44 || v30 // [bool_or] local w4_items = [{k = ".", v = -65536}, {k = "\t\n\r", v = 0x8000}, {k = "\r\n", v = v41}] foreach (item in w4_items) if (item.v > -32770) item.v *= 2147483648 local w4 = 0 foreach (item in w4_items) w4 += item.v // [foreach_array_tables_modify] return w4 } let u2 = u2_fn() // [nested_func depth=2] let u3_fn = function() { const w0 = false // [const_bool] const function [pure] w1_fn(x) { return x + 0xFF } const w1_a = -2147483649 const w1 = w1_fn(w1_a) // [const_chain] let w2 = [-4, -4294967296, 128] w2.insert(1, v20) w2.remove(0) // [array_insert_remove] let w3 = v39.tofloat() // [int_to_float] let w4_inner = function(...) { local s = 0 foreach (v in vargv) s += v return s } let w4_outer = function(a, ...) { return a + w4_inner(vargv.len(), a) } let w4 = w4_outer(p0, v2) // [vargv_forward] let w5 = v45 && p0 // [logical_and_val] return w5 } let u3 = u3_fn() // [nested_func depth=2] return u3 } let v46 = v46_fn(v35, v20) // [nested_func depth=1] let v47_fn = function(a, b, combine = {op = @(x, y) x + y, identity = -65535}) { return combine.op(a, b) + combine.identity } let v47_add = v47_fn(v42, v20) let v47_mul = v47_fn(v42, v20, {op = @(x, y) x * y, identity = 1000}) let v47 = v47_add + v47_mul // [default_param_fn_field_call] local v48_cfg = {a = {val = v32 + -256}, b = {val = v32 - 257}} let v48 = v44 ? (v48_cfg?.a?.val ?? 2147483648) : (v48_cfg?.b?.val ?? 1000) // [conditional_deep_path] let v49 = ",".join(v37.map(@(v) $"{_sv(v)}")) // [string_join] let v50 = v3.toupper() // [string_toupper] let v51_tbl = {a = v6, b = v26} let v51 = v51_tbl.a + v51_tbl.b // [destruct_table] let v52_C = class { _v = 0 constructor(xv) { this._v = xv } function _call(_env, arg) { return this._v + arg } } let v52_obj = v52_C(v43) let v52 = v52_obj(-65536) // [meta_call] let v53 = v13 >= v23 // [int_cmp_ge] local v54 = 0 local v54_i = 0 while (v54_i < -1) { v54_i++ if (v54_i % 2 == 0) continue v54 += v33 } // [while_continue_skip] local v55 = v4 v55 /= v12 // [modify_div_int_unsafe] enum S1680_E6 { X = -32769, Y = 32767, Z = 2147483649 } let v56 = S1680_E6.X + S1680_E6.Y + S1680_E6.Z // [enum_explicit] let v57 = v3.len() > 4 ? v3.slice(1, 3) : v3 // [string_slice_both] let v58_fn = function(p0, p1, p2) { local u0_a = (v56 & 0xFFFF) + 1 local u0_b = (v31 & 0xFFFF) + 1 while (u0_b != 0) { local u0_tmp = u0_b u0_b = u0_a % u0_b u0_a = u0_tmp } let u0 = u0_a // [int_gcd] let u1 = v29.split_by_chars("\n") // [string_split_by_chars] let u2 = clone v37 u2.replace_with(v37.map(@(v) v)) // [array_replace_with] return u0 } let v58 = v58_fn(v54, v47, v28) // [nested_func depth=1] let v59 = v37?[-4294967298] ?? 0x7FFFFFFF // [nullsafe_array_index] local v60 = v1 ++v60 // [prefix_inc] local v61_sum = 0 let v61_add = function(x) { v61_sum = v61_sum + x } v61_add(v28) v61_add(v12) v61_add(v28 + v12) let v61 = v61_sum // [closure_accumulator] let v62_v = v33 & 0xFFFF let v62 = v62_v > 0 && (v62_v & (v62_v - 1)) == 0 // [int_pow2_check] let v63_fn = function(x, acc = []) { acc.append(x) return acc.len() } let v63_a = v63_fn(v22) let v63_b = v63_fn(v22 + 1) let v63 = v63_a + v63_b // [default_param_mutable_shared] local v64 = -9223372036854775807 for (local v64_i = 0; v64_i < 2; v64_i++) { v64 += v41 } // [modify_in_loop] let v65_fn = function(p0, p1, p2) { let u0_fn = function({da, db = "true"}) { return $"{_sv(da)}{_sv(db)}" } let u0 = u0_fn({da = v64, db = v49}) // [destruct_param_nested_table] let u1_f = @(x) x * -4 let u1_g = @(x) x + 2147483648 let u1 = u1_f(u1_g(v1)) // [func_compose] let u2_lo = -2 let u2_hi = u2_lo + 256 let u2 = v32 < u2_lo ? u2_lo : (v32 > u2_hi ? u2_hi : v32) // [int_clamp] let u3_C = class { _v = 0 constructor(xv) { this._v = xv } function _call(_env, arg) { return this._v + arg } } let u3_obj = u3_C(v26) let u3 = u3_obj(2147483647) // [meta_call] local u4 = p2 & 7 do { u4-- } while (u4 > 0) // [do_while_dec] let u5_fn = function(q0, q1) { let w0 = q0.len() // [string_len] let w1_fn = function({da, db = " "}) { return $"{_sv(da)}{_sv(db)}" } let w1 = w1_fn({da = v55, db = v21}) // [destruct_param_nested_table] let w2_fn = function(r0, r1, r2) { let z0_fn = @[nodiscard] (x) x + -129 let z0 = z0_fn(p2) // [func_nodiscard] let z1_f = @(x) x * -258 let z1_g = @(x) x + 0x80000000 let z1 = z1_f(z1_g(v1)) // [func_compose] local z2 = 0 foreach (idx, v in v37) { if (idx % 2 != 0) continue if (typeof v == "integer") z2 += v } // [foreach_array_continue] return z2 } let w2 = w2_fn(v40, v2, v33) // [nested_func depth=3] let w3 = v64 <= v17 // [int_cmp_le] let w4_C = class { xv = 0 constructor(x) { this.xv = x } function _typeof() { return "mytype" } } let w4 = typeof w4_C(v32) // [meta_typeof] let w5 = q0.endswith(v3) // [string_endswith] return w5 } let u5 = u5_fn(v27, v26) // [nested_func depth=2] let u6 = (1e-40 * (-2.5)) // [float_literal] let u7 = u6 + v1.tofloat() // [float_add_int] return u7 } let v65 = v65_fn(v24, v13, v9) // [nested_func depth=1] let v66 = clone v37 v66.replace_with(v37.map(@(v) v)) // [array_replace_with] let v67_C = class { _n = 0 constructor(n) { this._n = n & 7 } function _nexti(idx) { local next = idx == null ? 0 : idx + 1 return next < this._n ? next : null } function _get(idx) { return idx * 2147483647 } } let v67_obj = v67_C(v17) local v67 = 0 foreach (_i, v in v67_obj) v67 += v // [meta_nexti] let v68_fn = function(x, ops = [@(v) v + -65537, @(v) v * 2, @(v) v - -4294967296]) { local r = x foreach (op in ops) r = op(r) return r } let v68_a = v68_fn(v43) let v68_b = v68_fn(v43, [@(v) v, @(v) v + -257]) let v68 = v68_a + v68_b // [default_param_array_of_fns] let v69_fn = function({dx, dy = 0}, [da, db]) { return dx + dy + da + db } let v69 = v69_fn({dx = v11}, [v33, v55]) // [destruct_param_mixed] let v70_fn = function(x, ctx = { cls = class { v = 0; constructor(n) { this.v = n } }, wrap = @(inst) inst.v + 0 }) { return ctx.wrap(ctx.cls(x)) } let v70 = v70_fn(v40) // [default_param_table_class] let v71_inner = function(...) { local s = 0 foreach (v in vargv) s += v return s } let v71_outer = function(a, ...) { return a + v71_inner(vargv.len(), a) } let v71 = v71_outer(v45, v58) // [vargv_forward] let v72 = v2 != 0 ? v45 / v2 : -4294967298 // [int_div_safe] let v73 = null // [null_var] let v74 = (@(x) -x)(v55) // [lambda_negate] function [nodiscard] v75_fn(x) { return x + 32768 } let v75 = v75_fn(v7) // [named_func_nodiscard] let v76_fn = function() { let u0_fn = function(x, ops = [@(v) v + 0, @(v) v * 32768, @(v) v - -1]) { local r = x foreach (op in ops) r = op(r) return r } let u0_a = u0_fn(v31) let u0_b = u0_fn(v31, [@(v) v, @(v) v + 1]) let u0 = u0_a + u0_b // [default_param_array_of_fns] let u1_fn = function(a: int, b: int): int { return a + b } let u1 = u1_fn(v17, v13) // [typed_function_params] local u2 = -4294967295 if (v62) { u2 = u2 + v51 u2 = u2 + -2147483650 } // [if_accum] let u3_fn = function(x, opts = {f = @(v) v + 'Z', scale = 65536}) { return opts.f(x) * opts.scale } let u3_a = u3_fn(u2) let u3_b = u3_fn(u2, {f = @(v) v * -129, scale = 0}) let u3 = u3_a + u3_b // [default_param_fn_in_table] return u3 } let v76 = v76_fn() // [nested_func depth=1] local v77 = v29 v77 += v50 // [modify_string_concat] let v78 = v30 ? v60 : v8 // [ternary_int] let v79_t = freeze({x = v42, y = 32768}) let v79 = v79_t.x + v79_t.y // [freeze_nested] let v80 = v10.lstrip() // [string_lstrip] let v81_fn = function(p0) { local u0: float = v65 // [typed_float_var] local u1 = 0 switch (v31 & 3) { case 0: case 1: u1 = -32770; break case 2: u1 = 255; break default: u1 = -255 } // [switch_fallthrough] let u2 = typeof v58 == "integer" // [typeof_int_check] local u3 = 0 foreach (_c in v21) u3++ // [foreach_string_chars] return u3 } let v81 = v81_fn(v61) // [nested_func depth=1] local v82 = v45 v82 += v54 v82 -= v78 v82 *= -258 // [modify_chain] let v83 = v69 <= v54 // [int_cmp_le] let v84_C = class { _v = 0 constructor(xv) { this._v = xv } function _call(_env, arg) { return this._v + arg } } let v84_obj = v84_C(v68) let v84 = v84_obj('9') // [meta_call] local v85_items = [{k = "\r\n", v = 0xFF}, {k = @"raw_string", v = -128}, {k = "test", v = v73}] foreach (item in v85_items) if (item.v > 0x8000) item.v *= 2147483649 local v85 = 0 foreach (item in v85_items) v85 += item.v // [foreach_array_tables_modify] let v86 = v82 | v39 // [int_bitor] local v87 = v0 v87 += -2 // [modify_add_const] let v88_C = class { _bv = 0 constructor(xb) { this._bv = xb } function _get(idx) { return this._bv + idx } } let v88_obj = v88_C(v64) let v88 = v88_obj[0xDEADBEEF] // [meta_get] let v89_fn = function({da, db}) { return da + db } let v89 = v89_fn({da = v26, db = v28}) // [destruct_param_table] let v90 = v53 ? v50 : v77 // [ternary_str] let v91_fn = function([da, db = -1]) { return da * db } let v91 = v91_fn([v60]) // [destruct_param_array_default] let v92 = v37.findindex(@(val) val != null) ?? -2147483649 // [array_findindex] let v93_fn = function(a, b = -4294967297) { return a + b } let v93 = v93_fn(v84) // [default_param_one] let v94_C = class { xv = 0 constructor(x) { this.xv = x } function _cmp(o) { return this.xv <=> o.xv } } let v94 = v94_C(v32) < v94_C(v15) // [meta_cmp] let v95 = v61 < v48 // [int_cmp_lt] local v96 = 0 for (local v96_i = 0; v96_i < 0; v96_i++) { v96 = v96 + v47 } // [for_sum] local v97 = false try { v97 = v28 == v20 } catch (_e) {} // [any_cmp_eq] local v98_items = [1000, null, {fn = @(x) x * -65535, tag = "\uffff"}, null] let v98_entry = v98_items?[2] let v98 = v98_entry != null ? v98_entry.fn(v26) : 0x100 // [mixed_array_fn_call] let v99_C = class { _items = [] function add(x) { this._items.append(x) } function sum() { local s = 0 foreach (v in this._items) s += v return s } } let v99_obj = v99_C() v99_obj.add(v6) v99_obj.add(v6 + 1) let v99 = v99_obj.sum() // [class_with_array] let v100 = (function() { let g = (function() { for (local i = 0; i < 7; i++) yield i })() local s = 0 while (g.getstatus() == "suspended") { s += resume g } return s })() // [coroutine_sum] let v101 = v80.endswith(v10) // [string_endswith] let v102 = v53.tostring() // [bool_to_string] let v103_fn = function(x, s, cfg = { fmt = @(n, t) $"{n}:{t}", limits = {lo = 32769, hi = 32768} }) { local clamped = x < cfg.limits.lo ? cfg.limits.lo : x > cfg.limits.hi ? cfg.limits.hi : x return cfg.fmt(clamped, s) } let v103 = v103_fn(v8, v50) // [default_param_config_nested] let v104 = (((-129) + (-130))).tofloat() // [float_literal] // --- dump pool --- function _prn_t(_t) { let _a = []; _t.each(function(_k, _v) { _a.append(_sv(_k)); _a.append(_sv(_v)) }); _a.sort(); foreach (i, _v in _a) { if (i >= 10) { print("..."); break }; if (i > 0) print(", "); print(_v) }; } function _prn_a(_a) { foreach (i, _v in _a) { if (i >= 10) { print("..."); break }; if (i > 0) print(", "); print(_v) }; } print($"v0={v0}\n") print($"v1={v1}\n") print($"v2={v2}\n") print($"v3={v3}\n") print($"v4={v4}\n") print($"v5={v5}\n") print($"v6={v6}\n") print($"v7={v7}\n") print($"v8={v8}\n") print($"v9={v9}\n") print($"v10={v10}\n") print($"v11={v11}\n") print($"v12={v12}\n") print($"v13={v13}\n") print($"v14={v14}\n") print($"v15={v15}\n") print($"v16={v16}\n") print($"v17={v17}\n") print($"v18={v18}\n") print($"v19={v19}\n") print($"v20={v20}\n") print($"v21={v21}\n") print($"v22={v22}\n") print($"v23={v23}\n") print($"v24={v24}\n") print($"v25={v25}\n") print($"v26={v26}\n") print($"v27={v27}\n") print($"v28={v28}\n") print($"v29={v29}\n") print($"v30={v30}\n") print($"v31={v31}\n") print($"v32={v32}\n") print($"v33={v33}\n") print($"v34={v34}\n") print($"v35={v35}\n") print($"v36={v36}\n") print("v37=["); _prn_a(v37); print("]\n") print($"v38={v38}\n") print($"v39={v39}\n") print($"v40={v40}\n") print($"v41={v41}\n") print($"v42={v42}\n") print($"v43={v43}\n") print($"v44={v44}\n") print($"v45={v45}\n") print($"v46={v46}\n") print($"v47={v47}\n") print($"v48={v48}\n") print($"v49={v49}\n") print($"v50={v50}\n") print($"v51={v51}\n") print($"v52={v52}\n") print($"v53={v53}\n") print($"v54={v54}\n") print($"v55={v55}\n") print($"v56={v56}\n") print($"v57={v57}\n") print($"v58={v58}\n") print($"v59={v59}\n") print($"v60={v60}\n") print($"v61={v61}\n") print($"v62={v62}\n") print($"v63={v63}\n") print($"v64={v64}\n") print($"v65={v65}\n") print("v66=["); _prn_a(v66); print("]\n") print($"v67={v67}\n") print($"v68={v68}\n") print($"v69={v69}\n") print($"v70={v70}\n") print($"v71={v71}\n") print($"v72={v72}\n") print($"v73={v73}\n") print($"v74={v74}\n") print($"v75={v75}\n") print($"v76={v76}\n") print($"v77={v77}\n") print($"v78={v78}\n") print($"v79={v79}\n") print($"v80={v80}\n") print($"v81={v81}\n") print($"v82={v82}\n") print($"v83={v83}\n") print($"v84={v84}\n") print($"v85={v85}\n") print($"v86={v86}\n") print($"v87={v87}\n") print($"v88={v88}\n") print($"v89={v89}\n") print($"v90={v90}\n") print($"v91={v91}\n") print($"v92={v92}\n") print($"v93={v93}\n") print($"v94={v94}\n") print($"v95={v95}\n") print($"v96={v96}\n") print($"v97={v97}\n") print($"v98={v98}\n") print($"v99={v99}\n") print($"v100={v100}\n") print($"v101={v101}\n") print($"v102={v102}\n") print($"v103={v103}\n") print($"v104={v104}\n") } catch(e) { print(e) } print("===\n") ================================================ FILE: testData/exec/weird/many_locals.out ================================================ division by zero=== ================================================ FILE: testData/proposed_optimizations/clone_newslot_vs_merge.nut ================================================ /* Proposed optimization: replace t.__merge({small literal table}) with clone + newslot sequence, avoiding temporary table allocation. Pattern: local r = t.__merge({y = 5, f = 5.3}) Can be optimized to: local r = clone t r.y <- 5 r.f <- 5.3 This is faster because __merge allocates a temporary table literal on every call, while clone + newslot avoids that overhead. */ let {clock} = require("datetime") function profile_it(cnt, f) { local res = 0 for (local i = 0; i < cnt; ++i) { let start = clock() f() let measured = clock() - start if (i == 0 || measured < res) res = measured } return res / 1.0 } let t = { x = 4, z = 5, u = 123 } function test_clone_newslot() { local cnt = 1000000 local r = null while (--cnt) { r = clone t r.y <- 5 r.f <- 5.3 } return r } function test_merge() { local cnt = 1000000 local r = null while (--cnt) { r = t.__merge({y = 5, f = 5.3}) } return r } let a = test_clone_newslot() let b = test_merge() assert(a.x == b.x && a.z == b.z && a.u == b.u && a.y == b.y && a.f == b.f) const numTests = 10 let profile = @(name, func) println("".concat(name, ", ", profile_it(numTests, func), $", {numTests}")) profile("\"clone_newslot\"", test_clone_newslot) profile("\"merge\"", test_merge) ================================================ FILE: testData/proposed_optimizations/filter_map_folding.nut ================================================ /* This test shows that it is possible to optimize map().filter().map().each() into loop Which is faster */ let {clock} = require("datetime") function profile_it(cnt, f) {//for quirrel version local res = 0 for (local i = 0; i < cnt; ++i) { let start = clock() f() let measured = clock() - start if (i == 0 || measured < res) res = measured } return res / 1.0 } //prepare testdata let testdata = freeze(array(17*120005).reduce(function(a, _, i) { a[i]<-{key = i}; return a;}, {})) function log(d){ foreach(k, v in d) println($"{k} = {v}") } // 'classic' functional code let test_filterampreduce = @() testdata .map(@(v) v.key) //extract func .filter(@(r) r%17==0) //filter func .map(@(r, i) (r*1000-i)/30) .reduce(@(res, v) res+(v%2==0 ? 1 : -1)*v, 0) //reduce func // 'fully unrolled' functional code. can be theoritically done for function that are declared inplace, like code above function test_loop_max() { local res = 0 foreach (i, v in testdata) { local r = v.key if (r%17 != 0) continue r = (r*1000 - i)/30 res += (r%2==0 ? 1 : -1)*r } return res } // 'partially unrolled' functional code (can be use for any functions used in map\filter\reduce\each hidden loops) function test_loop_func() { local res = 0 let extract = @(v) v.key let filter = @(v) v%17==0 let map2 = @(v, i) (v*1000-i)/30 let reduce = @(res, v) res+(v%2==0 ? 1 : -1)*v foreach (i, v in testdata) { local r = extract(v) if (!filter(r)) continue r = map2(r, i) res = reduce(res, r) } return res } const numTests = 20 let profile = @(name, func) println("".concat(name, ", ", profile_it(numTests, func), $", {numTests}")) assert(test_filterampreduce() == test_loop_max() && test_filterampreduce() == test_loop_func()) profile("\"map_filter_map_reduce\"", test_filterampreduce) profile("\"test_loop_max\"", test_loop_max) profile("\"test_loop_func\"", test_loop_func) ================================================ FILE: testData/proposed_optimizations/filter_map_folding2.nut ================================================ /* This test shows that it is possible to optimize map().filter().map().each() into loop Which is faster */ let {clock} = require("datetime") function profile_it(cnt, f) {//for quirrel version local res = 0 for (local i = 0; i < cnt; ++i) { let start = clock() f() let measured = clock() - start if (i == 0 || measured < res) res = measured } return res / 1.0 } //prepare testdata let testdata = freeze(array(17*120005).reduce(function(a, _, i) { a[i]<-{key = i}; return a;}, {})) function log(d){ foreach(k, v in d) println($"{k} = {v}") } // 'classic' functional code let test_filtermap = @() testdata .map(@(v) v.key) //extract func .filter(@(r) r%17==0) //filter func let test_filtermap_opt = function() { let mapfunc = @(v) v.key let filterfunc = @(v) v%17==0 return testdata .map(function(v) { let res = mapfunc(v) if (!filterfunc(res)) throw null return res }) } //extract func let test_map = @() testdata .map(function(v) { let a = v.key if (a%17 != 0) throw null return a }) //extract func // 'fully unrolled' functional code. can be theoritically done for function that are declared inplace, like code above function test_loop_max() { let res = [] foreach (i, v in testdata) { let r = v.key if (r%17 != 0) continue res.append(r) } return res } const numTests = 20 let profile = @(name, func) println("".concat(name, ", ", profile_it(numTests, func), $", {numTests}")) let [a,b,c,d] = [test_filtermap, test_map, test_filtermap, test_filtermap_opt].map(@(f) f().reduce(@(a,b) a+b)) assert(a == b && b == c && c==d ) profile("\"plain_map_filter\"", test_filtermap) profile("\"test_stupid_simple_optimization\"", test_filtermap_opt) profile("\"map_replace_filter_with_throw\"", test_map) profile("\"test_loop_max\"", test_loop_max) ================================================ FILE: testData/proposed_optimizations/strings_folding.nut ================================================ /* This test shows that it is possible to convert plus and\or concat operations to string substituation Which is faster */ let {clock} = require("datetime") function profile_it(cnt, f) {//for quirrel version local res = 0 for (local i = 0; i < cnt; ++i) { let start = clock() f() let measured = clock() - start if (i == 0 || measured < res) res = measured } return res / 1.0 } const foo = "foo" local bar = "bar" local num = 1234 local somestring = " somestring" let plus = @(i) foo + "_" +bar+"_"+num +"_" +somestring + i let concat = @(i) "".concat(foo, "_",bar,"_",num,"_",somestring, i) let fstring = @(i) $"{foo}_{bar}_{num}_ {somestring} {i}" function test(iters, func) { local res local i = iters while(--i){ res = func(i) } return res } const numTests = 20 const iters = 200000 function profile(name, func){ println("".concat(name, ", ", profile_it(numTests, func), $", {numTests}")) } profile("\"string plus\"", @() test(iters, plus)) profile("\"string concat\"",@() test(iters, concat)) profile("\"string format\"", @() test(iters, fstring)) ================================================ FILE: testData/static_analyzer/.sqconfig ================================================ ; for w238_sqconfig.nut function_result_must_be_utilized = _must_be_utilized ; for w239_sqconfig.nut function_should_return_bool_prefix = returnBool ; ; for w260_table_sqconfig.nut function_should_return_something_prefix = make ================================================ FILE: testData/static_analyzer/200_nullc.diag.txt ================================================ WARNING: w200 (potentially-nulled-ops) Comparison operation with potentially nullable expression. testData/static_analyzer/200_nullc.nut:7:44 let _x = (((item?.isPrimaryBuy ?? false) > (res?.isPrimaryBuy ?? null) ? item : res)) ^------------------------- WARNING: w305 (relative-bool-cmp) Relative comparison of non-boolean with boolean. It is a potential runtime error. testData/static_analyzer/200_nullc.nut:7:12 let _x = (((item?.isPrimaryBuy ?? false) > (res?.isPrimaryBuy ?? null) ? item : res)) ^--------------------------------------------------------- ================================================ FILE: testData/static_analyzer/200_nullc.nut ================================================ function foo() {} let item = foo() let res = foo() let _x = (((item?.isPrimaryBuy ?? false) > (res?.isPrimaryBuy ?? null) ? item : res)) //-file:useless-null-coalescing ================================================ FILE: testData/static_analyzer/function_rt_detect.diag.txt ================================================ WARNING: w226 (return-different-types) Function can return different types. testData/static_analyzer/function_rt_detect.nut:6:0 function _foo(p) { // EXPECTED ^ if (p) ================================================ FILE: testData/static_analyzer/function_rt_detect.nut ================================================ // -file:plus-string function trim(s) { return s } function _foo(p) { // EXPECTED if (p) return 1 else return 1 + trim("A") + trim("B") + trim("C").join(true) + 9 } function _bar(p) { // FP if (p) return "1" else return 2 + trim("A") + trim("B") + trim("C").join(true) + 8 } ================================================ FILE: testData/static_analyzer/logic_ops_paren.diag.txt ================================================ WARNING: w202 (and-or-paren) Priority of the '&&' operator is higher than that of the '||' operator. Perhaps parentheses are missing? testData/static_analyzer/logic_ops_paren.nut:5:8 let a = x && y || z ^---------- let b = x || y && z WARNING: w202 (and-or-paren) Priority of the '&&' operator is higher than that of the '||' operator. Perhaps parentheses are missing? testData/static_analyzer/logic_ops_paren.nut:6:8 let a = x && y || z let b = x || y && z ^---------- let c = (x && y) || z WARNING: w203 (bitwise-bool-paren) Result of bitwise operation used in boolean expression. Perhaps parentheses are missing? testData/static_analyzer/logic_ops_paren.nut:10:8 let e = x || y | z ^--------- let f = x || y & z WARNING: w203 (bitwise-bool-paren) Result of bitwise operation used in boolean expression. Perhaps parentheses are missing? testData/static_analyzer/logic_ops_paren.nut:11:8 let e = x || y | z let f = x || y & z ^--------- let g = x && y | z WARNING: w203 (bitwise-bool-paren) Result of bitwise operation used in boolean expression. Perhaps parentheses are missing? testData/static_analyzer/logic_ops_paren.nut:12:8 let f = x || y & z let g = x && y | z ^--------- let h = x && y & z WARNING: w203 (bitwise-bool-paren) Result of bitwise operation used in boolean expression. Perhaps parentheses are missing? testData/static_analyzer/logic_ops_paren.nut:13:8 let g = x && y | z let h = x && y & z ^--------- ================================================ FILE: testData/static_analyzer/logic_ops_paren.nut ================================================ //-file:declared-never-used //-file:const-in-bool-expr local x = 1, y = 2, z = 3; let a = x && y || z let b = x || y && z let c = (x && y) || z let d = x || (y && z) let e = x || y | z let f = x || y & z let g = x && y | z let h = x && y & z ================================================ FILE: testData/static_analyzer/module_foo.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/module_foo.nut ================================================ return { foo = @(a, b) a+b } ================================================ FILE: testData/static_analyzer/nullcheck_ternary.diag.txt ================================================ WARNING: w248 (access-potentially-nulled) 'expression' can be null, but is used as a container without checking. testData/static_analyzer/nullcheck_ternary.nut:9:4 foo(p.z) ^ ================================================ FILE: testData/static_analyzer/nullcheck_ternary.nut ================================================ if (__name__ == "__analysis__") return function foo(_p) {} let x = {} let p = foo(1) ? x?.x : x.y foo(p.z) //-file:expr-cannot-be-null ================================================ FILE: testData/static_analyzer/pattern_class_check.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/pattern_class_check.nut ================================================ function foo() { return class {} } let x = foo() let _c = class(x) {} ================================================ FILE: testData/static_analyzer/pattern_effect_from_call.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/pattern_effect_from_call.nut ================================================ function foo(_p) {} function bar(_p) {} function qux() {} let val = qux()?.z bar(foo(val) ? val.tointeger() : 0) ================================================ FILE: testData/static_analyzer/pattern_forloop_merge.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/pattern_forloop_merge.nut ================================================ //-file:declared-never-used // What does this test check??? function findlast(str, substr, startidx=0) { local ret = null for(local i=startidx; i 320 ? @() 20 : function() { return 30 } ================================================ FILE: testData/static_analyzer/pattern_loop_state.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/pattern_loop_state.nut ================================================ if (__name__ == "__analysis__") return function foo() {} local found = false let diap = foo() foreach (_seg in diap) { // if (seg[1] - seg[0] < size) // continue if (found) continue found = true } return 0 ================================================ FILE: testData/static_analyzer/pattern_param_check.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/pattern_param_check.nut ================================================ function _appls(ar) { foreach (_x in ar) { let a = @(perk) (perk?.items ?? []).len() > 0 let _b = @(perk) !a(perk) } } ================================================ FILE: testData/static_analyzer/w190.diag.txt ================================================ WARNING: w190 (paren-is-function-call) '(' on a new line parsed as function call. testData/static_analyzer/w190.nut:9:4 foo(10) (x.bar) ? 10 : 20 ^ ] AN ERROR HAS OCCURRED [attempt to call 'string'] CALLSTACK *FUNCTION [__main__()] testData/static_analyzer/w190.nut:8 LOCALS [x] TABLE={bar="T"} [foo] CLOSURE=FN:foo [vargv] ARRAY=[] [this] NULL ================================================ FILE: testData/static_analyzer/w190.nut ================================================ //-file:suspicious-bracket function foo(_p) { return "X" } let x = { bar = "T" } let _a = [ foo(10) (x.bar) ? 10 : 20 ] ================================================ FILE: testData/static_analyzer/w192.diag.txt ================================================ WARNING: w192 (statement-on-same-line) Next statement on the same line after 'then' statement. testData/static_analyzer/w192.nut:4:9 if (test == 3) a = 6; a = 10 ^ WARNING: w192 (statement-on-same-line) Next statement on the same line after 'else' statement. testData/static_analyzer/w192.nut:10:9 else a = 6; a = 10 ^ WARNING: w192 (statement-on-same-line) Next statement on the same line after 'while loop body' statement. testData/static_analyzer/w192.nut:14:11 while (a > b) a = 0; b = 20; ^ WARNING: w315 (invalid-indentation) Invalid indentation. Pay attention to lines 3 and 4. testData/static_analyzer/w192.nut:4:9 if (test == 3) a = 6; a = 10 ^----- WARNING: w315 (invalid-indentation) Invalid indentation. Pay attention to lines 4 and 7. testData/static_analyzer/w192.nut:7:0 if (test == 5) ^ a = 7 WARNING: w315 (invalid-indentation) Invalid indentation. Pay attention to lines 7 and 10. testData/static_analyzer/w192.nut:10:9 else a = 6; a = 10 ^----- WARNING: w315 (invalid-indentation) Invalid indentation. Pay attention to lines 10 and 13. testData/static_analyzer/w192.nut:13:0 while (a > b) ^ a = 0; b = 20; WARNING: w315 (invalid-indentation) Invalid indentation. Pay attention to lines 13 and 14. testData/static_analyzer/w192.nut:14:11 while (a > b) a = 0; b = 20; ^----- WARNING: w315 (invalid-indentation) Invalid indentation. Pay attention to lines 14 and 18. testData/static_analyzer/w192.nut:18:0 print(a + b); ^----------- ================================================ FILE: testData/static_analyzer/w192.nut ================================================ local test = true local a = 1, b = 2 if (test == 3) a = 6; a = 10 if (test == 5) a = 7 else a = 6; a = 10 while (a > b) a = 0; b = 20; print(a + b); ================================================ FILE: testData/static_analyzer/w200.diag.txt ================================================ WARNING: w200 (potentially-nulled-ops) Comparison operation with potentially nullable expression. testData/static_analyzer/w200.nut:2:10 function fn(mod, wpUnitRank) { //-declared-never-used return (mod?.reqRank > wpUnitRank) ^----------- } ================================================ FILE: testData/static_analyzer/w200.nut ================================================ function fn(mod, wpUnitRank) { //-declared-never-used return (mod?.reqRank > wpUnitRank) } ================================================ FILE: testData/static_analyzer/w200_3wcmp.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w200_3wcmp.nut ================================================ // This check is (intentionally?) disabled for <=> operator. // If needed, change isBoolRelationOperator() to isRelationOperator() // in checkPotentiallyNullableOperands() function in analyzer function _itemsSorter(a, b) { // -return-different-types a = a?.item b = b?.item if (!a || !b) return a <=> b return 42 } ================================================ FILE: testData/static_analyzer/w200_arith.diag.txt ================================================ WARNING: w200 (potentially-nulled-ops) Arithmetic operation with potentially nullable expression. testData/static_analyzer/w200_arith.nut:4:10 local x = {y = 2} local a = x?.y - 8 ^--- print(a) ================================================ FILE: testData/static_analyzer/w200_arith.nut ================================================ //-file:expr-cannot-be-null local x = {y = 2} local a = x?.y - 8 print(a) ================================================ FILE: testData/static_analyzer/w200_arith_deep.diag.txt ================================================ WARNING: w200 (potentially-nulled-ops) Arithmetic operation with potentially nullable expression. testData/static_analyzer/w200_arith_deep.nut:5:10 local z = x?.y local a = z - 8 ^ print(a) ================================================ FILE: testData/static_analyzer/w200_arith_deep.nut ================================================ //-file:expr-cannot-be-null local x = {y = 2} local z = x?.y local a = z - 8 print(a) ================================================ FILE: testData/static_analyzer/w200_arith_plus_eq.diag.txt ================================================ WARNING: w200 (potentially-nulled-ops) Arithmetic operation with potentially nullable expression. testData/static_analyzer/w200_arith_plus_eq.nut:5:0 local a = x?.z a -= 10 ^ print(a) ================================================ FILE: testData/static_analyzer/w200_arith_plus_eq.nut ================================================ //-file:expr-cannot-be-null local x = {y = 2, z = 1} local a = x?.z a -= 10 print(a) ================================================ FILE: testData/static_analyzer/w200_special_func_name.diag.txt ================================================ WARNING: w200 (potentially-nulled-ops) Arithmetic operation with potentially nullable expression. testData/static_analyzer/w200_special_func_name.nut:6:7 return ::a.b.c.indexof("x") + 6; ^------------------- ================================================ FILE: testData/static_analyzer/w200_special_func_name.nut ================================================ if (__name__ == "__analysis__") return //-file:undefined-global return ::a.b.c.indexof("x") + 6; ================================================ FILE: testData/static_analyzer/w200_static_memo_expr.diag.txt ================================================ WARNING: w200 (potentially-nulled-ops) Comparison operation with potentially nullable expression. testData/static_analyzer/w200_static_memo_expr.nut:2:10 function fn(mod, wpUnitRank) { //-declared-never-used return (static(mod?.reqRank) > wpUnitRank) // -w316 ^------------------ } ================================================ FILE: testData/static_analyzer/w200_static_memo_expr.nut ================================================ function fn(mod, wpUnitRank) { //-declared-never-used return (static(mod?.reqRank) > wpUnitRank) // -w316 } ================================================ FILE: testData/static_analyzer/w200_stringconcat.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w200_stringconcat.nut ================================================ //-file:expr-cannot-be-null //-file:plus-string //-file:decl-in-expression // Potentially nullable operand check (w200) explicitly allows nulls in string concatenation let o = {} let s = o?.x let ss = "a" + o + s //-declared-never-used ================================================ FILE: testData/static_analyzer/w203.diag.txt ================================================ WARNING: w203 (bitwise-bool-paren) Result of bitwise operation used in boolean expression. Perhaps parentheses are missing? testData/static_analyzer/w203.nut:11:18 if (condition1 || condition2 || condition3 | condition4) ^------------------------------------ print("ok") ================================================ FILE: testData/static_analyzer/w203.nut ================================================ if (__name__ == "__analysis__") return //-file:undefined-global local condition1 = ::x local condition2 = ::y local condition3 = ::z local condition4 = ::w if (condition1 || condition2 || condition3 | condition4) print("ok") ================================================ FILE: testData/static_analyzer/w204.diag.txt ================================================ WARNING: w204 (bitwise-apply-to-bool) The '&' or '|' operator is applied to boolean type. You've probably forgotten to include parentheses or intended to use the '&&' or '||' operator. testData/static_analyzer/w204.nut:2:6 function foo(x){ //-declared-never-used if (x & 15 == 8) ^---------- print("ok") ================================================ FILE: testData/static_analyzer/w204.nut ================================================ function foo(x){ //-declared-never-used if (x & 15 == 8) print("ok") } ================================================ FILE: testData/static_analyzer/w205-2.diag.txt ================================================ WARNING: w205 (unreachable-code) Unreachable code after 'return'. testData/static_analyzer/w205-2.nut:3:2 return let t = { //-declared-never-used ^ a = 1 ================================================ FILE: testData/static_analyzer/w205-2.nut ================================================ function y() { //-declared-never-used return let t = { //-declared-never-used a = 1 } } ================================================ FILE: testData/static_analyzer/w205.diag.txt ================================================ WARNING: w205 (unreachable-code) Unreachable code after 'return'. testData/static_analyzer/w205.nut:3:4 return callback() ^--------- } WARNING: w315 (invalid-indentation) Invalid indentation. Pay attention to lines 2 and 3. testData/static_analyzer/w205.nut:3:4 return callback() ^--------- } ================================================ FILE: testData/static_analyzer/w205.nut ================================================ function x(callback) { //-declared-never-used return callback() } ================================================ FILE: testData/static_analyzer/w206.diag.txt ================================================ WARNING: w206 (assigned-twice) Variable is assigned twice successively. testData/static_analyzer/w206.nut:4:0 tab.y = 7 tab.x = 8 ^-------- ================================================ FILE: testData/static_analyzer/w206.nut ================================================ local tab = {x=0, y=0, z=0} tab.x = 6 tab.y = 7 tab.x = 8 ================================================ FILE: testData/static_analyzer/w206_arith.diag.txt ================================================ WARNING: w206 (assigned-twice) Variable is assigned twice successively. testData/static_analyzer/w206_arith.nut:11:0 x /= foo(); x = foo(); ^-------- print(x) ================================================ FILE: testData/static_analyzer/w206_arith.nut ================================================ function foo() { return 20 } local x = 10 x = foo(); x += foo(); x -= foo(); x *= foo(); x %= foo(); x /= foo(); x = foo(); print(x) x = foo() print("") x = foo() ================================================ FILE: testData/static_analyzer/w208.diag.txt ================================================ WARNING: w208 (potentially-nulled-assign) Assignment to potentially nullable expression. testData/static_analyzer/w208.nut:2:0 local x = { z = {y = 3}} x.z?.y <- 6 ^----- print(x.z.y) ================================================ FILE: testData/static_analyzer/w208.nut ================================================ local x = { z = {y = 3}} x.z?.y <- 6 print(x.z.y) ================================================ FILE: testData/static_analyzer/w209.diag.txt ================================================ WARNING: w209 (assigned-to-itself) The variable is assigned to itself. testData/static_analyzer/w209.nut:3:0 x.y = (((x.y))) ^----------- ================================================ FILE: testData/static_analyzer/w209.nut ================================================ local x = {y = 3} x.y = (((x.y))) ================================================ FILE: testData/static_analyzer/w210.diag.txt ================================================ WARNING: w210 (potentially-nulled-index) Potentially nullable expression used as array index. testData/static_analyzer/w210.nut:6:9 ::f <- x[y?["a"]] ^------ ================================================ FILE: testData/static_analyzer/w210.nut ================================================ //-file:expr-cannot-be-null local x = { b = 1 } local y = { a = "b" } ::f <- x[y?["a"]] ================================================ FILE: testData/static_analyzer/w210_complex.diag.txt ================================================ WARNING: w210 (potentially-nulled-index) Potentially nullable expression used as array index. testData/static_analyzer/w210_complex.nut:13:6 foo(x[y]) ^ foo(x?[y]) WARNING: w210 (potentially-nulled-index) Potentially nullable expression used as array index. testData/static_analyzer/w210_complex.nut:17:12 foo(x.y?[10].y?[y]) foo(x.y.z.u[y]) ^ foo(x.y?.z.u[y]) ================================================ FILE: testData/static_analyzer/w210_complex.nut ================================================ if (__name__ == "__analysis__") return function foo(_x) { return 20 } let y = null let x = {} foo(x[y]) foo(x?[y]) foo(x?.z[y]) foo(x.y?[10].y?[y]) foo(x.y.z.u[y]) foo(x.y?.z.u[y]) //-file:expr-cannot-be-null ================================================ FILE: testData/static_analyzer/w210_deep.diag.txt ================================================ WARNING: w210 (potentially-nulled-index) Potentially nullable expression used as array index. testData/static_analyzer/w210_deep.nut:7:9 ::f <- x[index] ^---- ================================================ FILE: testData/static_analyzer/w210_deep.nut ================================================ //-file:expr-cannot-be-null local y = { a = "b" } local index = y?["a"] local x = { b = 1 } ::f <- x[index] ================================================ FILE: testData/static_analyzer/w210_def.diag.txt ================================================ WARNING: w210 (potentially-nulled-index) Potentially nullable expression used as array index. testData/static_analyzer/w210_def.nut:7:31 local buildBtnParams = ::kwarg(function(icon=null, option=null, count_list=null, counterFunc=null){ //-declared-never-used local list = ::contactsLists[count_list ?? option].list ^------------------- counterFunc = counterFunc ?? function(_){ return list } ================================================ FILE: testData/static_analyzer/w210_def.nut ================================================ if (__name__ == "__analysis__") return //-file:undefined-global local buildBtnParams = ::kwarg(function(icon=null, option=null, count_list=null, counterFunc=null){ //-declared-never-used local list = ::contactsLists[count_list ?? option].list counterFunc = counterFunc ?? function(_){ return list } return counterFunc }) return buildBtnParams ================================================ FILE: testData/static_analyzer/w211.diag.txt ================================================ WARNING: w211 (duplicate-case) Duplicate case value. testData/static_analyzer/w211.nut:13:7 case MODE.MODE_2: print("2"); break; case MODE.MODE_1: print("3"); break; ^---------- default: ================================================ FILE: testData/static_analyzer/w211.nut ================================================ #allow-switch-statement local MODE = { MODE_1 = 1 MODE_2 = 2 MODE_3 = 3 } local x = 1 switch(x) { case MODE.MODE_1: print("1"); break; case MODE.MODE_2: print("2"); break; case MODE.MODE_1: print("3"); break; default: print("0") break; } ================================================ FILE: testData/static_analyzer/w212.diag.txt ================================================ WARNING: w212 (duplicate-if-expression) Detected pattern 'if (A) {...} else if (A) {...}'. Branch unreachable. testData/static_analyzer/w212.nut:9:9 print("2") else if (x == 1) // OK ^----- print("3") WARNING: w212 (duplicate-if-expression) Detected pattern 'if (A) {...} else if (A) {...}'. Branch unreachable. testData/static_analyzer/w212.nut:26:8 ; if (x == 1) // OK ^----- print("3"); WARNING: w212 (duplicate-if-expression) Detected pattern 'if (A) {...} else if (A) {...}'. Branch unreachable. testData/static_analyzer/w212.nut:35:9 print("2") else if (x == 1) // OK ^----- print("3");;; ================================================ FILE: testData/static_analyzer/w212.nut ================================================ local x = 1 function foo() { return 10 } if (x == 1) print("1") else if (x == 2) print("2") else if (x == 1) // OK print("3") else { x = foo() print("4"); if (x == 1) // WRONG print("5"); } if (x == 1) print("1") else if (x == 2) print("2") else { ; ; if (x == 1) // OK print("3"); } if (x == 1) print("1") else if (x == 2) print("2") else if (x == 1) // OK print("3");;; ================================================ FILE: testData/static_analyzer/w213.diag.txt ================================================ WARNING: w213 (then-and-else-equals) 'then' statement is equivalent to 'else' statement. testData/static_analyzer/w213.nut:6:7 } else { ^ x /= 4 ================================================ FILE: testData/static_analyzer/w213.nut ================================================ function _foo(x) { if (x == 1) { x /= 4 print(x) } else { x /= 4 print(x) } } ================================================ FILE: testData/static_analyzer/w214.diag.txt ================================================ WARNING: w214 (operator-returns-same-val) Both branches of operator '<> ? <> : <>' are equivalent. testData/static_analyzer/w214.nut:13:7 ::x <- test ? REPLAY.SKIRMISH : REPLAY.SKIRMISH ^--------------------------------------- ================================================ FILE: testData/static_analyzer/w214.nut ================================================ if (__name__ == "__analysis__") return //-file:undefined-global local test = ::f enum REPLAY { SKIRMISH = 2 BATTLE = 3 //-declared-never-used } ::x <- test ? REPLAY.SKIRMISH : REPLAY.SKIRMISH ================================================ FILE: testData/static_analyzer/w214_static_memo_expr.diag.txt ================================================ WARNING: w214 (operator-returns-same-val) Both branches of operator '<> ? <> : <>' are equivalent. testData/static_analyzer/w214_static_memo_expr.nut:13:14 ::x <- static(test ? REPLAY.SKIRMISH : REPLAY.SKIRMISH) //-w316 ^--------------------------------------- ================================================ FILE: testData/static_analyzer/w214_static_memo_expr.nut ================================================ if (__name__ == "__analysis__") return //-file:undefined-global local test = ::f enum REPLAY { SKIRMISH = 2 BATTLE = 3 //-declared-never-used } ::x <- static(test ? REPLAY.SKIRMISH : REPLAY.SKIRMISH) //-w316 ================================================ FILE: testData/static_analyzer/w215.diag.txt ================================================ WARNING: w215 (ternary-priority) The '?:' operator has lower priority than the '+' operator. Perhaps the '?:' operator works in a different way than it was expected. testData/static_analyzer/w215.nut:5:10 local flag = true local b = 10 + flag ? 1 : 2 ^-------- print(b) ================================================ FILE: testData/static_analyzer/w215.nut ================================================ if (__name__ == "__analysis__") return local flag = true local b = 10 + flag ? 1 : 2 print(b) ================================================ FILE: testData/static_analyzer/w215_nullc.diag.txt ================================================ WARNING: w215 (ternary-priority) The '?:' operator has lower priority than the '??' operator. Perhaps the '?:' operator works in a different way than it was expected. testData/static_analyzer/w215_nullc.nut:8:9 let _u = o?.u ?? s ? s / r : 0.0 ^-------- ================================================ FILE: testData/static_analyzer/w215_nullc.nut ================================================ function foo() {} let o = {} let s = foo() let r = foo() let _u = o?.u ?? s ? s / r : 0.0 //-file:expr-cannot-be-null ================================================ FILE: testData/static_analyzer/w216.diag.txt ================================================ WARNING: w216 (same-operands) Left and right operands of '<' operator are the same. testData/static_analyzer/w216.nut:3:4 if (a.x < a.x) ^-------- print("x < y") ================================================ FILE: testData/static_analyzer/w216.nut ================================================ local a = {x=2, y=3} if (a.x < a.x) print("x < y") ================================================ FILE: testData/static_analyzer/w217_break.diag.txt ================================================ WARNING: w217 (unconditional-terminated-loop) Unconditional 'break' inside a loop. testData/static_analyzer/w217_break.nut:5:4 y--; break; ^---- z--; WARNING: w205 (unreachable-code) Unreachable code after 'break'. testData/static_analyzer/w217_break.nut:6:4 break; z--; ^-- } ================================================ FILE: testData/static_analyzer/w217_break.nut ================================================ function foo(x,y,z){ //-declared-never-used for (;;) { x++; y--; break; z--; } } ================================================ FILE: testData/static_analyzer/w217_complex.diag.txt ================================================ WARNING: w217 (unconditional-terminated-loop) Unconditional 'break' inside a loop. testData/static_analyzer/w217_complex.nut:30:12 foo() break ^---- } WARNING: w217 (unconditional-terminated-loop) Unconditional 'throw' inside a loop. testData/static_analyzer/w217_complex.nut:48:8 foo() throw "y" ^-------- } WARNING: w217 (unconditional-terminated-loop) Unconditional 'return' inside a loop. testData/static_analyzer/w217_complex.nut:70:4 return 0 ^------- } ================================================ FILE: testData/static_analyzer/w217_complex.nut ================================================ if (__name__ == "__analysis__") return #allow-switch-statement const MB = 3 function foo() {} function bar(_p) {} function qux(_p) {} let n = {} for (local offset = 1; offset < MB; offset++) { let idx = (n.v + offset) % MB if (foo() <= 0) continue foo() if (idx) bar(0.1) qux(idx) return true } for (;;) { foo() { foo() { foo() break } } } for (;;) { foo() try { foo() if (bar(10)) { qux(20) throw "x"; } else { qux(30); } throw "z" } catch (_e) { foo() throw "y" } foo() } for (;;) { foo() if (qux(20)) { break } switch (bar(30)) { case 1: foo() break; case 2: bar(4000) break; default: foo() } return 0 } for (;;) { foo() if (qux(20)) { break } switch (bar(30)) { case 1: foo() break; default: foo() continue } throw "3333" } ================================================ FILE: testData/static_analyzer/w217_cond_cont.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w217_cond_cont.nut ================================================ let data = {} let f = 10 let l = 10 let cl = {} let isArray = false function foo(_p1, _p2) {} function bar(_, __, ___, ____, _____) {} let res = [] foreach(k, r in data) { local d = r if (f <= l) { let isVisible = cl.value >= 0 && foo(r, k) if (!isVisible) continue cl(cl.value - 1) if (cl.value < 0) break } else { d = bar(r, l + 1, f, r, cl) if (d == null) continue } if (isArray) res.append(d) else res[k] <- d if (cl.value < 0) break // This is meaningless, but is allowed // We may use hasUnconditionalContinue in the analyzer to report this if needed continue } ================================================ FILE: testData/static_analyzer/w217_continue.diag.txt ================================================ WARNING: w217 (unconditional-terminated-loop) Unconditional 'continue' inside a loop. testData/static_analyzer/w217_continue.nut:7:4 ::h(::a, x) continue; ^------- x--; WARNING: w205 (unreachable-code) Unreachable code after 'continue'. testData/static_analyzer/w217_continue.nut:8:4 continue; x--; ^-- } while (x) ================================================ FILE: testData/static_analyzer/w217_continue.nut ================================================ //-file:undefined-global function foo(x) { //-declared-never-used do { if (::a == x) ::h(::a, x) continue; x--; } while (x) } ================================================ FILE: testData/static_analyzer/w217_ret.diag.txt ================================================ WARNING: w217 (unconditional-terminated-loop) Unconditional 'return' inside a loop. testData/static_analyzer/w217_ret.nut:8:4 return ^----- } ================================================ FILE: testData/static_analyzer/w217_ret.nut ================================================ //-file:undefined-global function foo(x){ //-declared-never-used while (x) { if (::a == x) ::h(::a, x) return } } ================================================ FILE: testData/static_analyzer/w217_throw.diag.txt ================================================ WARNING: w217 (unconditional-terminated-loop) Unconditional 'throw' inside a loop. testData/static_analyzer/w217_throw.nut:8:4 throw "err" ^---------- } ================================================ FILE: testData/static_analyzer/w217_throw.nut ================================================ //-file:undefined-global function _foo(x) { while (x) { if (::a == x) ::h(::a, x) throw "err" } } ================================================ FILE: testData/static_analyzer/w220.diag.txt ================================================ WARNING: w220 (potentially-nulled-container) 'foreach' on potentially nullable expression. testData/static_analyzer/w220.nut:2:15 function _foo(a) { foreach(x in a?.y()) { ^----- print(x) ================================================ FILE: testData/static_analyzer/w220.nut ================================================ function _foo(a) { foreach(x in a?.y()) { print(x) } } ================================================ FILE: testData/static_analyzer/w220_deep.diag.txt ================================================ WARNING: w220 (potentially-nulled-container) 'foreach' on potentially nullable expression. testData/static_analyzer/w220_deep.nut:3:15 local container = a?.y() foreach(x in container) { ^-------- print(x) ================================================ FILE: testData/static_analyzer/w220_deep.nut ================================================ function _foo(a) { local container = a?.y() foreach(x in container) { print(x) } } ================================================ FILE: testData/static_analyzer/w221.diag.txt ================================================ WARNING: w221 (result-not-utilized) Result of operation is not used. testData/static_analyzer/w221.nut:7:2 z-- ::x == y ^------- } ================================================ FILE: testData/static_analyzer/w221.nut ================================================ //-file:undefined-global local z function _foo(y) { ++z z-- ::x == y } ================================================ FILE: testData/static_analyzer/w221_delete.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w221_delete.nut ================================================ #allow-delete-operator let x = {} x.y <- "2" delete x.y ================================================ FILE: testData/static_analyzer/w222.diag.txt ================================================ WARNING: w222 (bool-as-index) Boolean used as array index. testData/static_analyzer/w222.nut:2:10 function _foo(a,x,y) { print(a[x < y]) ^---- } ================================================ FILE: testData/static_analyzer/w222.nut ================================================ function _foo(a,x,y) { print(a[x < y]) } ================================================ FILE: testData/static_analyzer/w222_deep.diag.txt ================================================ WARNING: w222 (bool-as-index) Boolean used as array index. testData/static_analyzer/w222_deep.nut:12:12 let index = x < y print(a[index]) ^---- } ================================================ FILE: testData/static_analyzer/w222_deep.nut ================================================ //-file:potentially-nullable-index //-file:undefined-global //-file:declared-never-used local x, y function b(...) {} // `let` binding with external function call // Should warn - let bindings are immutable and preserved function test_let(a) { let index = x < y print(a[index]) } // Direct use without any function call (baseline) // Doesn't work now!!! // Should warn - no function calls to potentially kill the value function test_local(a) { local index = x < y print(a[index]) } // Local variable, local function call, then external function call // Doesn't currently warn - the implementation is too complex. // Should warn - local function b() doesn't modify index function test_local_with_local_and_external_call(a) { local index = x < y b() print(a[index]) } // Local variable in nested block scope // Doesn't currently warn - the implementation is too complex // Should warn - nested scope doesn't affect the fix function test_nested_block_scope(a) { local index = x < y { print(a[index]) } } // Multiple external function calls // Doesn't currently warn - the implementation is too complex // Should warn - external calls don't affect function-local variables function test_multiple_external_calls(a) { local index = x < y print("before") print("middle") print(a[index]) } // Local variable used inside loop scope // Doesn't currently warn - the implementation is too complex // Should warn - loop scope is still within the function function test_loop_scope(a, arr) { local index = x < y foreach (_k, v in arr) { //-potentially-nullable if (v) print(a[index]) } } // Local function that doesn't modify the variable // Doesn't currently warn - the implementation is too complex // Should warn - inner() has empty body, doesn't modify index function test_local_function_no_modification(a) { local index = x < y function inner() {} //-declared-never-used inner() print(a[index]) } // Variable reassigned - should NOT warn function test_reassigned_variable(a) { local index = x < y index = 42 print(a[index]) } // Conditional assignment - should NOT warn (index could be non-boolean) function test_conditional_assignment(a, cond) { local index = x < y if (cond) index = 42 print(a[index]) } // Local variable, local function call capturing `index` as a free variable // Should NOT warn - the function can modify the variable function test_local_with_local_call_with_outer(a) { local index = x < y function localFunc() { index = 42 } localFunc() print(a[index]) } // A variation of above - should NOT warn function test_local_with_local_call_with_outer_2(a) { local index = x < y b(function() { index = 42 }) print(a[index]) } ================================================ FILE: testData/static_analyzer/w222_inside_detructure.diag.txt ================================================ WARNING: w222 (bool-as-index) Boolean used as array index. testData/static_analyzer/w222_inside_detructure.nut:3:12 function foo(a,x,y) { print(a[x < y]) ^---- } ================================================ FILE: testData/static_analyzer/w222_inside_detructure.nut ================================================ function fn([v = function foo(a,x,y) { print(a[x < y]) } ]) { return v } return fn ================================================ FILE: testData/static_analyzer/w223.diag.txt ================================================ WARNING: w223 (compared-with-bool) Comparison with boolean. testData/static_analyzer/w223.nut:4:4 local z = 2 if (x == y > z) ^--------- print("a") ================================================ FILE: testData/static_analyzer/w223.nut ================================================ local x = 1 local y = 1 local z = 2 if (x == y > z) print("a") if ((x > y) != z) print("b") if (x == (y > z)) print("c") ================================================ FILE: testData/static_analyzer/w223_method_is.diag.txt ================================================ WARNING: w223 (compared-with-bool) Comparison with boolean. testData/static_analyzer/w223_method_is.nut:11:4 if (r.foo() != s) // warns ^----------- print("d") ================================================ FILE: testData/static_analyzer/w223_method_is.nut ================================================ if (__name__ == "__analysis__") return let sec = 10000 let s = sec > 0 let r = ::external_data //-undefined-global if (r.isVisible() != s) // doesn't warn print("d") if (r.foo() != s) // warns print("d") ================================================ FILE: testData/static_analyzer/w224_then.diag.txt ================================================ WARNING: w224 (empty-body) 'then' branch of 'if' has an empty body. testData/static_analyzer/w224_then.nut:3:11 if (x == 5); ^ { ================================================ FILE: testData/static_analyzer/w224_then.nut ================================================ local x = 19 if (x == 5); { x++; return 0; } ================================================ FILE: testData/static_analyzer/w224_while.diag.txt ================================================ WARNING: w224 (empty-body) 'while' loop has an empty body. testData/static_analyzer/w224_while.nut:9:18 break; } while (x < 6) ; ^ } ================================================ FILE: testData/static_analyzer/w224_while.nut ================================================ //-file:statement-on-same-line //-file:invalid-indentation function foo(x){ //-declared-never-used while (x) { x++; if (x > 5) break; } while (x < 6) ; } ================================================ FILE: testData/static_analyzer/w225.diag.txt ================================================ WARNING: w225 (all-paths-return-value) Not all control paths return a value. testData/static_analyzer/w225.nut:1:0 function _x(y) { ^ if (y == 1) ================================================ FILE: testData/static_analyzer/w225.nut ================================================ function _x(y) { if (y == 1) return "y == 1" else if (y == 2) return "y == 2" } ================================================ FILE: testData/static_analyzer/w225_empty_stmt.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w225_empty_stmt.nut ================================================ // Ensure there are no false positives after the last semicolon let function _foo(filename) { assert(type(filename)=="string"); return {}; } let function _oneliner() { return []; } ================================================ FILE: testData/static_analyzer/w225_switch.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w225_switch.nut ================================================ #allow-switch-statement // No false positives in switch fallthrough function foo(_p) {} function _dataToBlk(data) { let blk = {} let dataType = foo(data) ? "DataBlock" : foo(data) switch (dataType) { case "null": return "__null" case "bool": case "integer": case "float": case "string": return data case "array": case "table": foreach (key, value in data) { foo(key) foo(value) } return "04" case "DataBlock": blk.setFrom(data) blk.__datablock <- true return "999" default: return "__unsupported " } } ================================================ FILE: testData/static_analyzer/w226.diag.txt ================================================ WARNING: w226 (return-different-types) Function can return different types. testData/static_analyzer/w226.nut:6:4 let function x(y) { ^ if (y == 1) ================================================ FILE: testData/static_analyzer/w226.nut ================================================ //-file:declared-never-used //-file:plus-string let o = {} let function x(y) { if (y == 1) return "y == 1" else if (y == 2) return y == 2 else return "unknown" } function foo(a, b) { return a.value ? b?.getEcsTemplates("ss") ?? [] : [] } let B = { D = {} DC = {} } function bar(w) { if (w?.c != null) return { c = w?.c } if (w?.b == null) return B.D return B.findvalue(@(bt) bt.p != null && (w?.bt.indexof(bt.p) ?? -1) == 0) ?? B.DC } function qux() { return o.isGG() ? "" : o.get1Txt() + " " + o.get2Txt() } function loc(a) { return a } function getabc() { let p = o.gts()?.aaa return p != null ? p + loc("dskjfhjs") : "" } const SVG_EXT = "xxx" let sahj = @(id, p = "ddd") (id in o) ? p + id + SVG_EXT : "" let _sumScore = o.reduce(@(res, v) res + (v?.score ?? 0)) ================================================ FILE: testData/static_analyzer/w227.diag.txt ================================================ WARNING: w227 (ident-hides-ident) variable 'a' hides parameter with the same name. testData/static_analyzer/w227.nut:4:4 local x = c local a = x ^---------- print(a) ================================================ FILE: testData/static_analyzer/w227.nut ================================================ function foo(a, c) { //-declared-never-used local b = function() { local x = c local a = x print(a) return x } return b() } ================================================ FILE: testData/static_analyzer/w227_external.diag.txt ================================================ WARNING: w227 (ident-hides-ident) parameter 'println' hides external binding with the same name. testData/static_analyzer/w227_external.nut:1:14 function _foo(println) { ^------ return println + 1 ================================================ FILE: testData/static_analyzer/w227_external.nut ================================================ function _foo(println) { return println + 1 } ================================================ FILE: testData/static_analyzer/w227_fn_with_same_param.diag.txt ================================================ WARNING: w227 (ident-hides-ident) parameter 'txt' hides function with the same name. testData/static_analyzer/w227_fn_with_same_param.nut:1:13 function txt(txt) {} //-declared-never-used ^-- ================================================ FILE: testData/static_analyzer/w227_fn_with_same_param.nut ================================================ function txt(txt) {} //-declared-never-used ================================================ FILE: testData/static_analyzer/w227_foreach_destr_shadow.diag.txt ================================================ WARNING: w228 (declared-never-used) parameter 'x' was declared but never used. testData/static_analyzer/w227_foreach_destr_shadow.nut:4:15 // through Visitor::visitDestructuringDecl so visitVarDecl runs for each name. function outer(x) { ^ function inner() { WARNING: w227 (ident-hides-ident) binding 'x' hides parameter with the same name. testData/static_analyzer/w227_foreach_destr_shadow.nut:6:14 function inner() { foreach ({x = 99} in [{x = 1}]) { ^----- println(x) ================================================ FILE: testData/static_analyzer/w227_foreach_destr_shadow.nut ================================================ // w227 (ident-hides-ident) must fire for a destructured foreach binding that // shadows an outer parameter / binding. Covers NameShadowingChecker routing // through Visitor::visitDestructuringDecl so visitVarDecl runs for each name. function outer(x) { function inner() { foreach ({x = 99} in [{x = 1}]) { println(x) } } inner() } outer(5) ================================================ FILE: testData/static_analyzer/w227_let_init_fun.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w227_let_init_fun.nut ================================================ if (__name__ == "__analysis__") return function foo(_p) {} // This is allowed, no warning here let setGroup = foo(function setGroup(_crew, _group, _onFinishCb) { }) setGroup() ================================================ FILE: testData/static_analyzer/w227_table.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w227_table.nut ================================================ if (__name__ == "__analysis__") return let getSoldierFace = @() 1 let _fx = 10 // This is not a name shadowing return { getSoldierFace = @() getSoldierFace() _fx = 100 } ================================================ FILE: testData/static_analyzer/w227_varargs.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w227_varargs.nut ================================================ // Var args are not conflicting function foo(a, ...) { return function (b, ...) { return a + b; } } foo("...") ================================================ FILE: testData/static_analyzer/w228.diag.txt ================================================ WARNING: w228 (declared-never-used) variable 'str' was declared but never used. testData/static_analyzer/w228.nut:2:8 function _x() { local str = "string" ^-- return ================================================ FILE: testData/static_analyzer/w228.nut ================================================ function _x() { local str = "string" return } ================================================ FILE: testData/static_analyzer/w228_2.diag.txt ================================================ WARNING: w228 (declared-never-used) variable 'f' was declared but never used. testData/static_analyzer/w228_2.nut:2:8 function _fn() { local f = 123 ^ local c = { f = 3 } ================================================ FILE: testData/static_analyzer/w228_2.nut ================================================ function _fn() { local f = 123 local c = { f = 3 } return c.f } ================================================ FILE: testData/static_analyzer/w228_3.diag.txt ================================================ WARNING: w228 (declared-never-used) variable 'f' was declared but never used. testData/static_analyzer/w228_3.nut:4:8 function _fn() { local f = 123 ^ local c = { f = 3 } ================================================ FILE: testData/static_analyzer/w228_3.nut ================================================ //-file:expr-cannot-be-null function _fn() { local f = 123 local c = { f = 3 } return c?.f } ================================================ FILE: testData/static_analyzer/w228_4.diag.txt ================================================ WARNING: w228 (declared-never-used) binding 'f' was declared but never used. testData/static_analyzer/w228_4.nut:1:4 let f = function foo() {} ^ let c = class {} WARNING: w228 (declared-never-used) binding 'c' was declared but never used. testData/static_analyzer/w228_4.nut:2:4 let f = function foo() {} let c = class {} ^ ================================================ FILE: testData/static_analyzer/w228_4.nut ================================================ let f = function foo() {} let c = class {} ================================================ FILE: testData/static_analyzer/w228_foreach_destr_default_uses.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w228_foreach_destr_default_uses.nut ================================================ // Identifier referenced inside a destructuring default-value expression must // be marked as used by the analyzer. Without the fix that routes // visitForeachStatement through valDestructuring->visit, the analyzer never // walks the default expression and would emit a spurious w228 // (declared-never-used) for `fallback_value`. function f() { let fallback_value = 42 foreach ({n = fallback_value} in [{}, {n = 1}]) { println(n) } } f() ================================================ FILE: testData/static_analyzer/w228_table.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w228_table.nut ================================================ // Nothing unused here function _foo(y) { let x = y return { x = x } } ================================================ FILE: testData/static_analyzer/w228_trivial.diag.txt ================================================ WARNING: w228 (declared-never-used) binding 'refreshOnWindowActivate' was declared but never used. testData/static_analyzer/w228_trivial.nut:5:9 function refreshOnWindowActivate(repeatAmount = 1, refreshPeriodSec = 10.0) { ^---------------------- readyRefreshTime = 0 ================================================ FILE: testData/static_analyzer/w228_trivial.nut ================================================ local readyRefreshTime = 1; local timeLeftToUpdate = 2; local refreshPeriod = 3; function refreshOnWindowActivate(repeatAmount = 1, refreshPeriodSec = 10.0) { readyRefreshTime = 0 timeLeftToUpdate = repeatAmount refreshPeriod = refreshPeriodSec } function xxx(_a, _b, _c) { } xxx(readyRefreshTime, timeLeftToUpdate, refreshPeriod) ================================================ FILE: testData/static_analyzer/w229.diag.txt ================================================ WARNING: w229 (copy-of-expression) Duplicate expression found inside the sequence of operations. testData/static_analyzer/w229.nut:2:16 local a ={x=0, y=0, z=1} if (a.x == 0 && a.y == 0 && a.x == 0) ^------------------- print("vector == zero") ================================================ FILE: testData/static_analyzer/w229.nut ================================================ local a ={x=0, y=0, z=1} if (a.x == 0 && a.y == 0 && a.x == 0) print("vector == zero") ================================================ FILE: testData/static_analyzer/w230_unused_import.diag.txt ================================================ WARNING: w230 (imported-never-used) Imported field 'cos' was never used. testData/static_analyzer/w230_unused_import.nut:2:24 // Test for unused imported fields diagnostic from "math" import sin, cos, tan ^-- from "string" import format, printf WARNING: w230 (imported-never-used) Imported field 'tan' was never used. testData/static_analyzer/w230_unused_import.nut:2:29 // Test for unused imported fields diagnostic from "math" import sin, cos, tan ^-- from "string" import format, printf WARNING: w230 (imported-never-used) Imported field 'printf' was never used. testData/static_analyzer/w230_unused_import.nut:3:29 from "math" import sin, cos, tan from "string" import format, printf ^----- from "io" import * WARNING: w230 (imported-never-used) Imported field 'datetime' was never used. testData/static_analyzer/w230_unused_import.nut:5:7 from "io" import * import "datetime" ^------- import "debug" as Debug WARNING: w230 (imported-never-used) Imported field 'Debug' was never used. testData/static_analyzer/w230_unused_import.nut:6:18 import "datetime" import "debug" as Debug ^---- ================================================ FILE: testData/static_analyzer/w230_unused_import.nut ================================================ // Test for unused imported fields diagnostic from "math" import sin, cos, tan from "string" import format, printf from "io" import * import "datetime" import "debug" as Debug // Only sin and format are used let _x = sin(0.5) let _str = format("value: %d", 42) // Unused imports (should trigger warnings): // - cos, tan (from math) // - printf (from string) // - datetime (whole module) // - Debug (whole module alias) ================================================ FILE: testData/static_analyzer/w231.diag.txt ================================================ WARNING: w231 (format-arguments-count) Format string: arguments count mismatch. testData/static_analyzer/w231.nut:3:20 local string = require("string") print(string.format("%d%%", 1, x)) ^----- ================================================ FILE: testData/static_analyzer/w231.nut ================================================ local x = 2 local string = require("string") print(string.format("%d%%", 1, x)) ================================================ FILE: testData/static_analyzer/w232_cascade.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w232_cascade.nut ================================================ // No always-true-or-false here function x(_i) {} function y(_i) {} function z(_i) {} function _foo(size, a, b) { if (a && b) return x(size) if (a) return y(size) if (b) return z(size) return null } ================================================ FILE: testData/static_analyzer/w232_false.diag.txt ================================================ WARNING: w232 (always-true-or-false) Expression is always 'false'. testData/static_analyzer/w232_false.nut:6:6 } print(a == ::XEnum.PARAM_A && a == ::XEnum.PARAM_B) ^------------------------------------------- ================================================ FILE: testData/static_analyzer/w232_false.nut ================================================ local a = 1 ::XEnum <- { PARAM_A = 1 PARAM_B = 2 } print(a == ::XEnum.PARAM_A && a == ::XEnum.PARAM_B) ================================================ FILE: testData/static_analyzer/w232_lambda.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w232_lambda.nut ================================================ // No always-true-or-false warning here function foo() {} let w = foo() local vv = true let wdata = foo() local _u if (type(w) == "table") { _u = w?.update ?? @(v) wdata(v) vv = w?.vv ?? vv } else { _u = w } if (vv) { foo() } ================================================ FILE: testData/static_analyzer/w232_not.diag.txt ================================================ WARNING: w232 (always-true-or-false) Expression is always 'false'. testData/static_analyzer/w232_not.nut:2:6 local i = {x = true} print(i.x && !i.x) ^---------- print(!i.x || i.x) WARNING: w232 (always-true-or-false) Expression is always 'true'. testData/static_analyzer/w232_not.nut:3:6 print(i.x && !i.x) print(!i.x || i.x) ^---------- ================================================ FILE: testData/static_analyzer/w232_not.nut ================================================ local i = {x = true} print(i.x && !i.x) print(!i.x || i.x) ================================================ FILE: testData/static_analyzer/w232_ter.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w232_ter.nut ================================================ // No always-true-or-false here function d() { } function _foo(p, bb, qq) { let uu = qq.value?[bb] return { kk = [ uu == null ? null : {} { text = uu ? p : d() } ] } } ================================================ FILE: testData/static_analyzer/w232_ternary.diag.txt ================================================ WARNING: w232 (always-true-or-false) Expression is always 'true'. testData/static_analyzer/w232_ternary.nut:2:12 //expect:w232 local foo = function() { } ? false : true ^------------- return foo ================================================ FILE: testData/static_analyzer/w232_ternary.nut ================================================ //expect:w232 local foo = function() { } ? false : true return foo ================================================ FILE: testData/static_analyzer/w232_true.diag.txt ================================================ WARNING: w232 (always-true-or-false) Expression is always 'true'. testData/static_analyzer/w232_true.nut:7:6 local i = 2 print(s.charAt(i) != ' ' || s.charAt(i) != '\t') ^---------------------------------------- ================================================ FILE: testData/static_analyzer/w232_true.nut ================================================ //expect:w232 local a = " " local s = { charAt = @(i) a[i] } local i = 2 print(s.charAt(i) != ' ' || s.charAt(i) != '\t') ================================================ FILE: testData/static_analyzer/w233.diag.txt ================================================ WARNING: w233 (const-in-bool-expr) Constant in a boolean expression. testData/static_analyzer/w233.nut:8:21 ::flags <- 0x2040 ::aspect_ratio <- (::flags && 0x40) ^-------------- } WARNING: w233 (const-in-bool-expr) Constant in a boolean expression. testData/static_analyzer/w233.nut:14:14 let flags = 0x2040 let _res = (flags && 0x40) ^------------ } WARNING: w233 (const-in-bool-expr) Constant in a boolean expression. testData/static_analyzer/w233.nut:21:16 local flags = 0x2040 local _res = (flags && 0x40) ^------------ } ================================================ FILE: testData/static_analyzer/w233.nut ================================================ if (__name__ == "__analysis__") return // Root table slots - should warn // Note: usage of the root table is deprecated { ::flags <- 0x2040 ::aspect_ratio <- (::flags && 0x40) } // Binding - should warn { let flags = 0x2040 let _res = (flags && 0x40) } // Local variable - should warn { local flags = 0x2040 local _res = (flags && 0x40) } // Indirect reference to a constant value through an unary operator - should not warn { ::f <- 10 local mask = -0x40 ::a <- (::f && !mask) } // Not a subject to "const-in-bool-expr" check - should not warn { function bar(_g) {} function foo() {} let { s, o } = foo() const Z = "xxs" let _seen = bar(@() o.value && s.value?[Z]) } ================================================ FILE: testData/static_analyzer/w234.diag.txt ================================================ WARNING: w234 (div-by-zero) Integer division by zero. testData/static_analyzer/w234.nut:5:8 let zero = 0 print(1 / (zero)) ^-------- } ================================================ FILE: testData/static_analyzer/w234.nut ================================================ //expect:w234 function foo() { //-declared-never-used let zero = 0 print(1 / (zero)) } ================================================ FILE: testData/static_analyzer/w234_outer.diag.txt ================================================ WARNING: w234 (div-by-zero) Integer division by zero. testData/static_analyzer/w234_outer.nut:12:8 function fn2() { //-declared-never-used print(1 / (y)) ^----- } ================================================ FILE: testData/static_analyzer/w234_outer.nut ================================================ //expect:w234 local x = 0 // can be modified before calling "fn1" function fn1() { //-declared-never-used print(1 / (x)) } let y = 0 // always equal to 0 function fn2() { //-declared-never-used print(1 / (y)) } ================================================ FILE: testData/static_analyzer/w235.diag.txt ================================================ WARNING: w235 (round-to-int) Result of division will be integer. testData/static_analyzer/w235.nut:3:19 ::aspect_ratio <- (1280 / (720)) ^---------- ================================================ FILE: testData/static_analyzer/w235.nut ================================================ //expect:w235 ::aspect_ratio <- (1280 / (720)) ================================================ FILE: testData/static_analyzer/w236.diag.txt ================================================ WARNING: w236 (shift-priority) Shift operator has lower priority. Perhaps parentheses are missing? testData/static_analyzer/w236.nut:4:30 function foo(berserkFx, state){ //-declared-never-used if (!berserkFx && (state & (1 << ::SCRIPT_STATE_USER_SHIFT + 4))) ^--------------------------------- print(1) ================================================ FILE: testData/static_analyzer/w236.nut ================================================ //expect:w236 ::SCRIPT_STATE_USER_SHIFT <- 2 function foo(berserkFx, state){ //-declared-never-used if (!berserkFx && (state & (1 << ::SCRIPT_STATE_USER_SHIFT + 4))) print(1) } ================================================ FILE: testData/static_analyzer/w238_heuristic.diag.txt ================================================ WARNING: w238 (named-like-should-return) Function name 'isLoggedIn' implies a return value, but its result is never used. testData/static_analyzer/w238_heuristic.nut:5:2 function _x() { ::isLoggedIn() //-undefined-global ^------------- } ================================================ FILE: testData/static_analyzer/w238_heuristic.nut ================================================ if (__name__ == "__analysis__") return function _x() { ::isLoggedIn() //-undefined-global } ================================================ FILE: testData/static_analyzer/w238_idname.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w238_idname.nut ================================================ if (__name__ == "__analysis__") return let state = {} let isHaHoo = state.foo() // prefixed with `is`, but accepts single boolean parameter - can "apply" this to an object // using a method call state.isRepa(isHaHoo) ================================================ FILE: testData/static_analyzer/w238_isis.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w238_isis.nut ================================================ function isRePre(_p) { return true } function is_re_pre() { return true } // Single boolean argument - can "apply" this boolean to some state // Should not warn isRePre(is_re_pre()) ================================================ FILE: testData/static_analyzer/w238_merge.diag.txt ================================================ WARNING: w238 (named-like-should-return) Function name '__merge' implies a return value, but its result is never used. testData/static_analyzer/w238_merge.nut:7:2 function _x() { ::a.__merge(::table2) ^-------------------- } ================================================ FILE: testData/static_analyzer/w238_merge.nut ================================================ if (__name__ == "__analysis__") return //-file:undefined-global function _x() { ::a.__merge(::table2) } ================================================ FILE: testData/static_analyzer/w238_sqconfig.diag.txt ================================================ WARNING: w238 (named-like-should-return) Function name '_must_be_utilized' implies a return value, but its result is never used. testData/static_analyzer/w238_sqconfig.nut:4:2 function x() { //-declared-never-used ::a._must_be_utilized(::table2); ^------------------------------ } ================================================ FILE: testData/static_analyzer/w238_sqconfig.nut ================================================ //expect:w238 function x() { //-declared-never-used ::a._must_be_utilized(::table2); } //-file:undefined-global ================================================ FILE: testData/static_analyzer/w239.diag.txt ================================================ WARNING: w239 (named-like-return-bool) Function name 'isLoggedIn' implies a return boolean type but not all control paths return boolean. testData/static_analyzer/w239.nut:3:9 function isLoggedIn() { //-declared-never-used ^--------- if (::userName == "") ================================================ FILE: testData/static_analyzer/w239.nut ================================================ //expect:w239 function isLoggedIn() { //-declared-never-used if (::userName == "") return false; if (::serverName == "") return return true; } //-file:undefined-global ================================================ FILE: testData/static_analyzer/w239_sqconfig.diag.txt ================================================ WARNING: w239 (named-like-return-bool) Function name 'returnBoolFunctionName' implies a return boolean type but not all control paths return boolean. testData/static_analyzer/w239_sqconfig.nut:3:9 function returnBoolFunctionName() { //-declared-never-used ^--------------------- if (::serverName == "") ================================================ FILE: testData/static_analyzer/w239_sqconfig.nut ================================================ //expect:w239 function returnBoolFunctionName() { //-declared-never-used if (::serverName == "") return return true; } //-file:undefined-global ================================================ FILE: testData/static_analyzer/w240.diag.txt ================================================ WARNING: w240 (null-coalescing-priority) The '??' operator has a lower priority than the '!=' operator (a??b > c == a??(b > c)). Perhaps the '??' operator works in a different way than it was expected. testData/static_analyzer/w240.nut:9:11 print(a ?? b != 1) // expected boolean ^----- ================================================ FILE: testData/static_analyzer/w240.nut ================================================ if (__name__ == "__analysis__") return //expect:w240 local a = ::x; // can be null local b = 1; print(a ?? b != 1) // expected boolean //-file:undefined-global ================================================ FILE: testData/static_analyzer/w241.diag.txt ================================================ WARNING: w241 (already-required) Module 'string' has been required already. testData/static_analyzer/w241.nut:7:13 local str1 = require("string") local str2 = require("string") ^---------------- print(str1, str2) ================================================ FILE: testData/static_analyzer/w241.nut ================================================ if (__name__ == "__analysis__") return //expect:w241 local str1 = require("string") local str2 = require("string") print(str1, str2) ================================================ FILE: testData/static_analyzer/w241_conditional.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w241_conditional.nut ================================================ // Is this test correct? // It tests for `already-required` warning function foo() {} let x = foo() let d = foo() let { _m=null } = require_optional("m.nut") let { _g=null } = d ? require_optional("a.nut") : x?.is_x ? require_optional("b.nut") : x?.is_s ? require_optional("c.nut") : x?.is_a ? require_optional("d.nut") : require_optional("a.nut") ================================================ FILE: testData/static_analyzer/w244.diag.txt ================================================ WARNING: w244 (used-from-static) Access 'this.y' from static member function. testData/static_analyzer/w244.nut:16:8 this.sss(); // FP 3 this.y = 30 // EXPECTED 1 ^----- } ================================================ FILE: testData/static_analyzer/w244.nut ================================================ function fex(x) { this.y = x // FP 1 } class _C { static function sss() {} function foo() { fex(@() this.y = 10) // FP 2 } static function bar() { this.sss(); // FP 3 this.y = 30 // EXPECTED 1 } } ================================================ FILE: testData/static_analyzer/w248.diag.txt ================================================ WARNING: w248 (access-potentially-nulled) 'a' can be null, but is used as a function without checking. testData/static_analyzer/w248.nut:11:4 } else { a() ^ } WARNING: w248 (access-potentially-nulled) 'a' can be null, but is used as a function without checking. testData/static_analyzer/w248.nut:14:7 return a() ^ ================================================ FILE: testData/static_analyzer/w248.nut ================================================ if (__name__ == "__analysis__") return //expect:w248 local a = ::x?.b local b = a if (b) { b() } else { a() } return a() //-file:undefined-global ================================================ FILE: testData/static_analyzer/w248_access.diag.txt ================================================ WARNING: w248 (access-potentially-nulled) 'expression' can be null, but is used as a container without checking. testData/static_analyzer/w248_access.nut:7:7 local a = ::x?.b return a.b[6] ^ ================================================ FILE: testData/static_analyzer/w248_access.nut ================================================ if (__name__ == "__analysis__") return //-file:undefined-global local a = ::x?.b return a.b[6] ================================================ FILE: testData/static_analyzer/w248_additional.diag.txt ================================================ WARNING: w248 (access-potentially-nulled) 'expression' can be null, but is used as a container without checking. testData/static_analyzer/w248_additional.nut:54:21 let outer = foo()?.captured let _closure = @() outer.accessed ^---- } WARNING: w248 (access-potentially-nulled) 'expression' can be null, but is used as a container without checking. testData/static_analyzer/w248_additional.nut:91:8 if (val == "string") { foo(obj.asString) ^-- } else if (val == "number") { WARNING: w248 (access-potentially-nulled) 'expression' can be null, but is used as a container without checking. testData/static_analyzer/w248_additional.nut:93:8 } else if (val == "number") { foo(obj.asNumber) ^-- } else if (obj != null) { WARNING: w248 (access-potentially-nulled) 'expression' can be null, but is used as a container without checking. testData/static_analyzer/w248_additional.nut:119:14 let nullable = foo()?.item let _arr = [nullable.prop] ^------- let _tbl = { key = nullable.prop2 } WARNING: w248 (access-potentially-nulled) 'expression' can be null, but is used as a container without checking. testData/static_analyzer/w248_additional.nut:120:21 let _arr = [nullable.prop] let _tbl = { key = nullable.prop2 } ^------- } WARNING: w248 (access-potentially-nulled) 'expression' can be null, but is used as a container without checking. testData/static_analyzer/w248_additional.nut:141:6 bar(arg) foo(arg.field) ^-- } WARNING: w248 (access-potentially-nulled) 'expression' can be null, but is used as a container without checking. testData/static_analyzer/w248_additional.nut:151:8 try { foo(val.attempt) ^-- } catch (_e) { WARNING: w248 (access-potentially-nulled) 'expression' can be null, but is used as a container without checking. testData/static_analyzer/w248_additional.nut:153:8 } catch (_e) { foo(val.fallback) ^-- } WARNING: w248 (access-potentially-nulled) 'expression' can be null, but is used as a container without checking. testData/static_analyzer/w248_additional.nut:175:6 if (!x) return // guard proves NEW x is non-null, but y has OLD value foo(y.field) // warning - y might be null (holds old x value) ^ } ================================================ FILE: testData/static_analyzer/w248_additional.nut ================================================ // Some cases that might logically seem like they should warn // are not flagged due to analyzer limitations in tracking complex flows. if (__name__ == "__analysis__") return function foo(_p = null) {} function bar(_x = null) {} //============================================================================= // Nested ternary expressions with nullable values //============================================================================= { let a = foo()?.x let b = foo()?.y // Nested ternary where both branches could be null // TRUE NEGATIVE: a and b are checked before access in their respective branches let _result = a != null ? (b != null ? a.val + b.val : a.val) : (b != null ? b.val : 0) // KNOWN LIMITATION: a.missing is in else branch where a wasn't checked, // but analyzer doesn't track ternary else-branch nullable state deeply let c = foo()?.z let _bad = c != null ? c.val : a.missing } //============================================================================= // Multiple reassignments in complex control flow //============================================================================= { local x = foo()?.data if (x != null) { x = foo()?.other // reassign to new nullable } // KNOWN LIMITATION: After if-block, analyzer loses precise tracking // of reassigned variables. x could be null but no warning issued. foo(x.field) } { // TRUE NEGATIVE: y is guaranteed non-null after null-coalescing assignment local y = foo()?.val y = y ?? { default = 1 } foo(y.default) } //============================================================================= // Closure captures of nullable variables //============================================================================= { // EXPECT WARNING: closure captures nullable without outer null-check let outer = foo()?.captured let _closure = @() outer.accessed } { // TRUE NEGATIVE: closure defined inside null-check block inherits the checked state let checked = foo()?.val if (checked != null) { let _safe_closure = @() checked.safe } } //============================================================================= // Loop-carried nullable dependencies //============================================================================= { // TRUE NEGATIVE inside loop: prev is explicitly checked before access local prev = null foreach (item in [1, 2, 3]) { if (prev != null) { foo(prev.data) } prev = foo(item)?.result } // KNOWN LIMITATION: After loop, prev could be null but analyzer // doesn't track loop exit state precisely foo(prev.final) } //============================================================================= // Switch/case with nullable checks (using if-else chain pattern) //============================================================================= { let val = foo()?.type let obj = foo()?.obj // EXPECT WARNING: obj not checked, only val is tested if (val == "string") { foo(obj.asString) } else if (val == "number") { foo(obj.asNumber) } else if (obj != null) { // TRUE NEGATIVE: obj explicitly checked in this branch foo(obj.other) } } //============================================================================= // Safe access chain followed by unsafe access //============================================================================= { // TRUE NEGATIVE: if deep is non-null, the entire chain must have succeeded, // meaning chain is non-null. Analyzer correctly infers this via extractReceiver. let chain = foo() let deep = chain?.level1?.level2?.level3 if (deep != null) { foo(chain.direct) } } //============================================================================= // Nullable in array/table literal (edge case) //============================================================================= { // EXPECT WARNING: accessing nullable in collection literal let nullable = foo()?.item let _arr = [nullable.prop] let _tbl = { key = nullable.prop2 } } //============================================================================= // Double negation pattern //============================================================================= { // TRUE NEGATIVE: !! coerces to boolean, so if (!!val) means val is truthy (non-null) let val = foo()?.x if (!!val) { foo(val.data) } } //============================================================================= // Nullable used as function argument then accessed //============================================================================= { // EXPECT WARNING: passing nullable to function is OK, but accessing member is not let arg = foo()?.param bar(arg) foo(arg.field) } //============================================================================= // Try-catch with nullable //============================================================================= { // EXPECT WARNING in both try and catch: nullable not checked in either block let val = foo()?.risky try { foo(val.attempt) } catch (_e) { foo(val.fallback) } } //============================================================================= // Assignment before null guard - alias tracking //============================================================================= { // TRUE NEGATIVE: y holds the same value as x. The guard on x proves // that value is non-null, and the analyzer tracks this aliasing relationship. local x = foo()?.val let y = x // y is an alias for x's value if (!x) return // guard proves the value (held by both x and y) is non-null foo(y.field) // no warning - y is non-null via alias tracking } { // EXPECT WARNING: x was reassigned before the guard, so y might hold old null value local x = foo()?.val let y = x // y holds x's current (possibly null) value x = foo()?.other // x reassigned to different value if (!x) return // guard proves NEW x is non-null, but y has OLD value foo(y.field) // warning - y might be null (holds old x value) } ================================================ FILE: testData/static_analyzer/w248_andand.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w248_andand.nut ================================================ function canEquipBothItems(_x) {} function _foo(data, onDropExceptionCb = null) { if (onDropExceptionCb != null && !canEquipBothItems(data) && data?.item != null) { onDropExceptionCb(data.item) } } ================================================ FILE: testData/static_analyzer/w248_andor.diag.txt ================================================ WARNING: w248 (access-potentially-nulled) 'expression' can be null, but is used as a container without checking. testData/static_analyzer/w248_andor.nut:4:26 let a = list != null && list.len() == 0 let b = list != null || list.len() == 0 ^--- ================================================ FILE: testData/static_analyzer/w248_andor.nut ================================================ function _riIsEmptyGroup(x) { let list = x?.list let a = list != null && list.len() == 0 let b = list != null || list.len() == 0 return a && b } ================================================ FILE: testData/static_analyzer/w248_array.diag.txt ================================================ WARNING: w248 (access-potentially-nulled) 'expression' can be null, but is used as a container without checking. testData/static_analyzer/w248_array.nut:7:7 local a = ::x?.b return a[6] ^ ================================================ FILE: testData/static_analyzer/w248_array.nut ================================================ if (__name__ == "__analysis__") return //-file:undefined-global local a = ::x?.b return a[6] ================================================ FILE: testData/static_analyzer/w248_assert.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w248_assert.nut ================================================ // assert() acts as a null-check if (__name__ == "__analysis__") return function foo() {} let o = foo() let c = o?.x assert(c != null) let _g = c.y // No warning - assert guarantees c != null at this point ================================================ FILE: testData/static_analyzer/w248_chain.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w248_chain.nut ================================================ if (__name__ == "__analysis__") return let x = {} let a = 10 if ((x.w.v?[a] ?? 0) == 0) { x.foo() // No warning - x is a table literal, always non-null } ================================================ FILE: testData/static_analyzer/w248_complex2.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w248_complex2.nut ================================================ function foo() {} let a = foo() let h = foo() let v = a?.v let c = h && (typeof v == "integer" || typeof v == "float") if (!c) { return { ct = @() null } } // After early return: v is guaranteed to be integer or float (not null) let _target = v.tointeger() // No warning - v is known to be a number type ================================================ FILE: testData/static_analyzer/w248_complex_key.diag.txt ================================================ WARNING: w248 (access-potentially-nulled) 'expression' can be null, but is used as a container without checking. testData/static_analyzer/w248_complex_key.nut:17:4 qux(a.z, h.z) ^ WARNING: w248 (access-potentially-nulled) 'expression' can be null, but is used as a container without checking. testData/static_analyzer/w248_complex_key.nut:17:9 qux(a.z, h.z) ^ ================================================ FILE: testData/static_analyzer/w248_complex_key.nut ================================================ if (__name__ == "__analysis__") return function foo() {} function bar(_a, _b) {} function qux(_a, _b) {} let { a = null, h = null } = foo() if (a?.x[h]) { bar(a.x[h], h.x) } qux(a.z, h.z) ================================================ FILE: testData/static_analyzer/w248_complexcond.diag.txt ================================================ WARNING: w248 (access-potentially-nulled) 'expression' can be null, but is used as a container without checking. testData/static_analyzer/w248_complexcond.nut:10:8 if (x == null && foo(2)) foo(x.y) ^ else ================================================ FILE: testData/static_analyzer/w248_complexcond.nut ================================================ if (__name__ == "__analysis__") return function foo(_p) {} local x = foo(1) if (x == null && foo(2)) foo(x.y) else foo(x.x) ================================================ FILE: testData/static_analyzer/w248_eq_get.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w248_eq_get.nut ================================================ if (__name__ == "__analysis__") return function foo(_p) {} let o = {} let u = o.value.findvalue(@(u) u.l == "999") let _x = u?.t != "a" || foo(u) ? null : u.t // No warning - reaching here means u?.t == "a", so u is non-null ================================================ FILE: testData/static_analyzer/w248_evaled.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w248_evaled.nut ================================================ if (__name__ == "__analysis__") return let t = {} function foo(_p) {} let sr = t.value.findvalue(@(s) s.s == 10 && (s?.e ?? 0) > 0 ) let r = sr != null let _timerObj = r ? foo({ ts = sr.e // No warning - r being true means sr != null }) : null ================================================ FILE: testData/static_analyzer/w248_getfield.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w248_getfield.nut ================================================ function foo() {} let o = foo() if (o?.w == null) return o.x = true // No warning - o is non-null after the early return guard ================================================ FILE: testData/static_analyzer/w248_in.diag.txt ================================================ WARNING: w248 (access-potentially-nulled) 'expression' can be null, but is used as a container without checking. testData/static_analyzer/w248_in.nut:5:9 return text.d ^--- } ================================================ FILE: testData/static_analyzer/w248_in.nut ================================================ function _foo(dict, text = null) { if (text in dict) return dict[text] return text.d } ================================================ FILE: testData/static_analyzer/w248_in_container.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w248_in_container.nut ================================================ function foo() {} function bar(_a) {} let { c = null} = foo() if ("xx" in c) { bar(c.xx) // No warning - "xx" in c implies c is non-null } ================================================ FILE: testData/static_analyzer/w248_not.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w248_not.nut ================================================ function foo(_p) {} let soldiers = {} let debriefing = foo(1) let squad = foo(2) let squads = foo(3) foreach (_soldierIdx, soldierStat in soldiers) { let soldierData = debriefing?.soldiers.items[soldierStat.soldierId] if (!soldierData) continue // Early exit if null - soldierData is non-null below if (squads?[soldierData.squadId] != squad) continue foo(soldierData.guid) } ================================================ FILE: testData/static_analyzer/w248_nullc_2.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w248_nullc_2.nut ================================================ if (__name__ == "__analysis__") return let t = {} let i = 10 let e = t.value?[i] if ((e?.a ?? false) && (e?.b ?? true)) { e.foo() // No warning - e must be non-null for condition to be true } ================================================ FILE: testData/static_analyzer/w248_nullc_3.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w248_nullc_3.nut ================================================ //-file:undefined-global let us = {} foreach (u in us) { if (!u.t.isAvailable()) continue let es = u.t.et let d = ::g?[::f(u)] if (d?[es] ?? true) continue // If d is null, we continue here d[es] <- ::q(u) // No warning - d is non-null (otherwise we'd have continued) } ================================================ FILE: testData/static_analyzer/w248_oror.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w248_oror.nut ================================================ function foo() {} let gg = foo() function _bar() { let { cfg = null, hasOwned = false } = gg.value if (cfg == null || hasOwned) return // Early exit if cfg is null OR hasOwned is true // After return: cfg != null && !hasOwned let { _c, _g } = cfg // No warning - cfg is non-null after the guard return } ================================================ FILE: testData/static_analyzer/w248_oror2.diag.txt ================================================ WARNING: w248 (access-potentially-nulled) 'nullNotH' can be null, but is used as a function without checking. testData/static_analyzer/w248_oror2.nut:5:2 onDoubleClickCb(itemDesc.__merge({ rectOrPos = event.targetRect })) nullNotH() ^------- } ================================================ FILE: testData/static_analyzer/w248_oror2.nut ================================================ function onDoubleClick(itemDesc, event, isLocked, isInteractive, onDoubleClickCb = null, nullNotH = null) { if (isLocked || !isInteractive || onDoubleClickCb == null || nullNotH != null) return onDoubleClickCb(itemDesc.__merge({ rectOrPos = event.targetRect })) nullNotH() } onDoubleClick(1, 2, 3, 4, 5) ================================================ FILE: testData/static_analyzer/w248_override.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w248_override.nut ================================================ function foo() {} local x = foo()?.x if (foo() || !x) return // After this, x is non-null let r = x x = null if (r.s) // Don't warn because r was assigned when x was non-null foo() ================================================ FILE: testData/static_analyzer/w248_relative_pred.diag.txt ================================================ WARNING: w200 (potentially-nulled-ops) Comparison operation with potentially nullable expression. testData/static_analyzer/w248_relative_pred.nut:13:9 if (x && y > 0) { ^ _r = b[10] / (y * 60) ================================================ FILE: testData/static_analyzer/w248_relative_pred.nut ================================================ if (__name__ == "__analysis__") return function foo() {} let x =foo() let y = x?.y let b = {} local _r = null if (x && y > 0) { _r = b[10] / (y * 60) } ================================================ FILE: testData/static_analyzer/w248_special_name_func.diag.txt ================================================ WARNING: w248 (access-potentially-nulled) 'expression' can be null, but is used as a container without checking. testData/static_analyzer/w248_special_name_func.nut:7:0 x.indexof(".").foo() ^------------- ================================================ FILE: testData/static_analyzer/w248_special_name_func.nut ================================================ if (__name__ == "__analysis__") return let x = {} x.indexof(".").foo() ================================================ FILE: testData/static_analyzer/w248_terminated_branch1.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w248_terminated_branch1.nut ================================================ //-file:potentially-nulled-index function foo() {} let a = foo() let b = foo() function _bar() { let c = a.value let res = {} foreach (g, d in b.value) { let t = c?[g].t let nu = c?[b].y if (t == null) continue // Early exit if t is null - t is non-null below if (t not in res) { res[t] <- {} // No w248 warning - t is non-null after continue guard res[nu] <- {} // nu is nullable but w210 is disabled for this file } res[t][g] <- d // No w248 warning - t is non-null res[nu][g] <- d // nu is nullable but w210 is disabled } return res } ================================================ FILE: testData/static_analyzer/w248_tyopeof1.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w248_tyopeof1.nut ================================================ function fo(debrData) { if (typeof debrData?.players != "table") return // Early exit if debrData is null or players isn't a table let players = {} foreach (id, player in debrData.players) players[id.tointeger()] <- player debrData.players = players } fo({}) ================================================ FILE: testData/static_analyzer/w248_tyopeof2.diag.txt ================================================ WARNING: w248 (access-potentially-nulled) 'f' can be null, but is used as a function without checking. testData/static_analyzer/w248_tyopeof2.nut:3:15 if ((typeof colorStr != "string" || (colorStr.len() != 8 && colorStr.len() != 6)) && (typeof f == "null") ) return f("first param must be string with len 6 or 8") ^ } ================================================ FILE: testData/static_analyzer/w248_tyopeof2.nut ================================================ function _foo(colorStr, f) { if ((typeof colorStr != "string" || (colorStr.len() != 8 && colorStr.len() != 6)) && (typeof f == "null") ) return f("first param must be string with len 6 or 8") } ================================================ FILE: testData/static_analyzer/w248_type_func.diag.txt ================================================ WARNING: w248 (access-potentially-nulled) 'f' can be null, but is used as a function without checking. testData/static_analyzer/w248_type_func.nut:3:15 if ((type(colorStr) != "string" || (colorStr.len() != 8 && colorStr.len() != 6)) && (type(f) == "null") ) return f("first param must be string with len 6 or 8") ^ } ================================================ FILE: testData/static_analyzer/w248_type_func.nut ================================================ function _foo(colorStr, f) { if ((type(colorStr) != "string" || (colorStr.len() != 8 && colorStr.len() != 6)) && (type(f) == "null") ) return f("first param must be string with len 6 or 8") } ================================================ FILE: testData/static_analyzer/w248_while_cond.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w248_while_cond.nut ================================================ function foo() {} let x = foo() local c = foo() let a = foo() let t = "skdhaljs" local d = 20 while (c && (c?.o ?? "") != "") { // c is non-null inside loop body c = a?[x.value?[t][c.o]] // c.o safe because loop condition ensures c non-null if ((c?.d ?? 0) > d) { d = c.d // No warning - c?.d > d means c is non-null (not using default 0) } else { d = d.xyz } } ================================================ FILE: testData/static_analyzer/w250_array.diag.txt ================================================ WARNING: w250 (cmp-with-container) Comparison with a array. testData/static_analyzer/w250_array.nut:8:10 let _x = (::a != []) ^-------- let _y = (::a != {}) WARNING: w250 (cmp-with-container) Comparison with a declaration. testData/static_analyzer/w250_array.nut:9:10 let _x = (::a != []) let _y = (::a != {}) ^-------- let _z = (::a != t) WARNING: w250 (cmp-with-container) Comparison with a declaration. testData/static_analyzer/w250_array.nut:11:11 let _z = (::a != t) let _xx = (::a == @ (v) v) ^------------- ================================================ FILE: testData/static_analyzer/w250_array.nut ================================================ if (__name__ == "__analysis__") return //-file:undefined-global let t = [] let _x = (::a != []) let _y = (::a != {}) let _z = (::a != t) let _xx = (::a == @ (v) v) ================================================ FILE: testData/static_analyzer/w250_container.diag.txt ================================================ WARNING: w250 (cmp-with-container) Comparison with a declaration. testData/static_analyzer/w250_container.nut:6:8 return (::a != {}) ^-------- ================================================ FILE: testData/static_analyzer/w250_container.nut ================================================ if (__name__ == "__analysis__") return //-file:undefined-global return (::a != {}) ================================================ FILE: testData/static_analyzer/w254.diag.txt ================================================ WARNING: w254 (bool-passed-to-strange) Boolean passed to 'in' operator. testData/static_analyzer/w254.nut:6:4 if (!"weapModSlotName" in ::item) ^--------------------------- return null ================================================ FILE: testData/static_analyzer/w254.nut ================================================ if (__name__ == "__analysis__") return //expect:w254 if (!"weapModSlotName" in ::item) return null //-file:undefined-global ================================================ FILE: testData/static_analyzer/w254_instanceof.diag.txt ================================================ WARNING: w254 (bool-passed-to-strange) Boolean passed to 'instanceof' operator. testData/static_analyzer/w254_instanceof.nut:6:4 local x = 10 if (x instanceof !"weapModSlotName") ^------------------------------ return null ================================================ FILE: testData/static_analyzer/w254_instanceof.nut ================================================ if (__name__ == "__analysis__") return //expect:w254 local x = 10 if (x instanceof !"weapModSlotName") return null ================================================ FILE: testData/static_analyzer/w254_notin.diag.txt ================================================ WARNING: w254 (bool-passed-to-strange) Boolean passed to 'in' operator. testData/static_analyzer/w254_notin.nut:6:4 if (!"weapModSlotName" not in ::item) ^------------------------------- return null ================================================ FILE: testData/static_analyzer/w254_notin.nut ================================================ if (__name__ == "__analysis__") return //expect:w254 if (!"weapModSlotName" not in ::item) return null //-file:undefined-global ================================================ FILE: testData/static_analyzer/w255.diag.txt ================================================ WARNING: w255 (duplicate-function) Duplicate function body. Consider functions 'onTimer2' and 'onTimer'. testData/static_analyzer/w255.nut:29:4 function onTimer(obj, dt) { ^ local curOffs = obj.cur_slide_offs.tofloat() ================================================ FILE: testData/static_analyzer/w255.nut ================================================ //expect:w255 //-file:undefined-global //-file:declared-never-used //-file:ident-hides-ident ::ClassName <- class { function onTimer2(obj, dt) { local curOffs = obj.cur_slide_offs.tofloat() // local pos = obj.getPos() local size = obj.getSize() local parentSize = obj.getParent().getSize() local speedCreditsScroll = (size[1] / parentSize[1] ) / ::timeToShowAll if (::pos[1] + ::size[1] < 0) { curOffs = -(0.9 * ::parentSize[1]).tointeger() if (obj?.inited == "yes") { ::on_credits_finish() return } else obj.inited="yes" } else curOffs += dt * parentSize[1] * speedCreditsScroll //* 720 / parentSize[1] / 0.9 obj.cur_slide_offs = ::format("%f", curOffs) obj.top = (-curOffs).tointeger().tostring() } function onTimer(obj, dt) { local curOffs = obj.cur_slide_offs.tofloat() // local pos = obj.getPos() local size = obj.getSize() local parentSize = obj.getParent().getSize() local speedCreditsScroll = (size[1] / parentSize[1] ) / ::timeToShowAll if (::pos[1] + ::size[1] < 0) { curOffs = -(0.9 * ::parentSize[1]).tointeger() if (obj?.inited == "yes") { ::on_credits_finish() return } else obj.inited="yes" } else curOffs += dt * parentSize[1] * speedCreditsScroll //* 720 / parentSize[1] / 0.9 obj.cur_slide_offs = ::format("%f", curOffs) obj.top = (-curOffs).tointeger().tostring() } } ================================================ FILE: testData/static_analyzer/w255_2.diag.txt ================================================ WARNING: w255 (duplicate-function) Duplicate function body. Consider functions 'onTimer2' and 'onTimer'. testData/static_analyzer/w255_2.nut:31:4 function onTimer(obj, dt) { ^ local curOffs = obj.cur_slide_offs.tofloat() ================================================ FILE: testData/static_analyzer/w255_2.nut ================================================ //expect:w255 //-file:undefined-global //-file:declared-never-used //-file:ident-hides-ident //expect:w255 local class ClassName { //-declared-never-used function onTimer2(obj, dt) { local curOffs = obj.cur_slide_offs.tofloat() // local pos = obj.getPos() local size = obj.getSize() local parentSize = obj.getParent().getSize() local speedCreditsScroll = (size[1] / parentSize[1] ) / ::timeToShowAll if (::pos[1] + ::size[1] < 0) { curOffs = -(0.9 * ::parentSize[1]).tointeger() if (obj?.inited == "yes") { ::on_credits_finish() return } else obj.inited="yes" } else curOffs += dt * parentSize[1] * speedCreditsScroll //* 720 / parentSize[1] / 0.9 obj.cur_slide_offs = ::format("%f", curOffs) obj.top = (-curOffs).tointeger().tostring() } function onTimer(obj, dt) { local curOffs = obj.cur_slide_offs.tofloat() // local pos = obj.getPos() local size = obj.getSize() local parentSize = obj.getParent().getSize() local speedCreditsScroll = (size[1] / parentSize[1] ) / ::timeToShowAll if (::pos[1] + ::size[1] < 0) { curOffs = -(0.9 * ::parentSize[1]).tointeger() if (obj?.inited == "yes") { ::on_credits_finish() return } else obj.inited="yes" } else curOffs += dt * parentSize[1] * speedCreditsScroll //* 720 / parentSize[1] / 0.9 obj.cur_slide_offs = ::format("%f", curOffs) obj.top = (-curOffs).tointeger().tostring() } } ================================================ FILE: testData/static_analyzer/w255_foreach_destr_distinct.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w255_foreach_destr_distinct.nut ================================================ // Two functions whose bodies differ only in the foreach destructuring pattern // must NOT be flagged as duplicate (w255) or similar (w258). Covers // diffForeach / cmpForeach including valDestructuring() in the comparison. function sum_xy(rows) { local total = 0 foreach ({x, y = 0} in rows) { total += x + y } return total } function sum_a_only(rows) { local total = 0 foreach ({a, b = 0} in rows) { total += a + b } return total } println(sum_xy([{x = 1, y = 2}])) println(sum_a_only([{a = 1, b = 2}])) ================================================ FILE: testData/static_analyzer/w256.diag.txt ================================================ WARNING: w256 (key-and-function-name) Key and function name are not the same ('_foo2' and 'bar2'). testData/static_analyzer/w256.nut:8:12 "1foo" : function bar1() {}, // FP 1, not id "_foo2" : function bar2() {}, // EXPECTED 1 ^----------------- "foo3" : function foo3() {}, // FP 2 WARNING: w256 (key-and-function-name) Key and function name are not the same ('foo4' and 'bar5'). testData/static_analyzer/w256.nut:11:9 foo4 = function bar5() {}, // EXPECTED 2 ^----------------- foo6 = function foo6() {}, // FP 3 WARNING: w256 (key-and-function-name) Key and function name are not the same ('foo7' and 'bar7'). testData/static_analyzer/w256.nut:14:13 ["foo7"] = function bar7() {}, // EXPECTED 3 ^----------------- ["8foo"] = function bar8() {}, // FP 4, not id WARNING: w256 (key-and-function-name) Key and function name are not the same ('qux' and 'fex'). testData/static_analyzer/w256.nut:21:10 tt.foo <- bar // FP 5 tt.qux <- function fex() {} // EXPECTED 4 ^---------------- tt["fk"] <- function uyte() {} // EXPECTED 5 WARNING: w256 (key-and-function-name) Key and function name are not the same ('fk' and 'uyte'). testData/static_analyzer/w256.nut:22:12 tt.qux <- function fex() {} // EXPECTED 4 tt["fk"] <- function uyte() {} // EXPECTED 5 ^----------------- tt["f:g:h"] <- function fgh() {} // FP 6 ================================================ FILE: testData/static_analyzer/w256.nut ================================================ //expect:w256 const C = 1 function bar() {} let tt = { "1foo" : function bar1() {}, // FP 1, not id "_foo2" : function bar2() {}, // EXPECTED 1 "foo3" : function foo3() {}, // FP 2 foo4 = function bar5() {}, // EXPECTED 2 foo6 = function foo6() {}, // FP 3 ["foo7"] = function bar7() {}, // EXPECTED 3 ["8foo"] = function bar8() {}, // FP 4, not id [C] = function bar9(_p) {} } tt.foo <- bar // FP 5 tt.qux <- function fex() {} // EXPECTED 4 tt["fk"] <- function uyte() {} // EXPECTED 5 tt["f:g:h"] <- function fgh() {} // FP 6 ================================================ FILE: testData/static_analyzer/w257.diag.txt ================================================ WARNING: w257 (duplicate-assigned-expr) Duplicate of the assigned expression. testData/static_analyzer/w257.nut:17:26 ] local numTextAnimations = [ ^ { prop=AnimProp.opacity, from=0.0, to=0.0, delay=0.0, duration=0.1, play=true easing=OutCubic } ================================================ FILE: testData/static_analyzer/w257.nut ================================================ if (__name__ == "__analysis__") return //expect:w257 //-file:undefined-global local OutCubic = 2 local AnimProp = ::aaa local numAnimations = [ { prop=AnimProp.opacity, from=0.0, to=0.0, delay=0.0, duration=0.1, play=true easing=OutCubic } { prop=AnimProp.scale, from=[2,2], to=[1,1], delay=0.1, duration=0.5, play=true easing=OutCubic } { prop=AnimProp.opacity, from=0.0, to=1.0, delay=0.1, duration=0.5, play=true easing=OutCubic } { prop=AnimProp.scale, from=[1,1], to=[2,2], delay=0.0, duration=0.1, playFadeOut=true easing=OutCubic } { prop=AnimProp.opacity, from=1.0, to=0.0, delay=0.1, duration=0.1, playFadeOut=true easing=OutCubic } ] local numTextAnimations = [ { prop=AnimProp.opacity, from=0.0, to=0.0, delay=0.0, duration=0.1, play=true easing=OutCubic } { prop=AnimProp.scale, from=[2,2], to=[1,1], delay=0.1, duration=0.5, play=true easing=OutCubic } { prop=AnimProp.opacity, from=0.0, to=1.0, delay=0.1, duration=0.5, play=true easing=OutCubic } { prop=AnimProp.scale, from=[1,1], to=[2,2], delay=0.0, duration=0.1, playFadeOut=true easing=OutCubic } { prop=AnimProp.opacity, from=1.0, to=0.0, delay=0.1, duration=0.1, playFadeOut=true easing=OutCubic } ] return [numAnimations, numTextAnimations] ================================================ FILE: testData/static_analyzer/w258.diag.txt ================================================ WARNING: w258 (similar-function) Function bodies are very similar. Consider functions 'updateEventLb' and 'updateEventLbSelfRow'. testData/static_analyzer/w258.nut:46:4 function updateEventLbSelfRow(requestData, id) { ^ local requestAction = (@(requestData, id) function () { ================================================ FILE: testData/static_analyzer/w258.nut ================================================ //expect:w257 //-file:undefined-global //-file:ident-hides-ident //-file:declared-never-used //-file:similar-assigned-expr //expect:w258 local canRequestEventLb = 10 let leaderboardsRequestStack = [] ::Aaa <- class { function requestUpdateEventLb(x) {} canRequestEventLb = false leaderboardsRequestStack = [] function updateEventLb(requestData, id) { local requestAction = (@(requestData, id) function () { local taskId = ::requestUpdateEventLb(requestData) if (taskId < 0) return canRequestEventLb = false ::add_bg_task_cb(taskId, (@(requestData, id) function() { ::handleLbRequest(requestData, id) if (leaderboardsRequestStack.len()) ::array_shift(leaderboardsRequestStack).fn() else canRequestEventLb = true })(requestData, id).bindenv(this)) })(requestData, id).bindenv(this) if (canRequestEventLb) return requestAction() if (id) foreach (index, request in leaderboardsRequestStack) if (id == request) leaderboardsRequestStack.remove(index) leaderboardsRequestStack.append({fn = requestAction, id = id}) } function updateEventLbSelfRow(requestData, id) { local requestAction = (@(requestData, id) function () { local taskId = ::requestEventLbSelfRow(requestData) if (taskId < 0) return canRequestEventLb = false ::add_bg_task_cb(taskId, (@(requestData, id) function() { ::handleLbSelfRowRequest(requestData, id) if (leaderboardsRequestStack.len()) ::array_shift(leaderboardsRequestStack).fn() else canRequestEventLb = true })(requestData, id).bindenv(this)) })(requestData, id).bindenv(this) if (canRequestEventLb) return requestAction() if (id) foreach (index, request in leaderboardsRequestStack) if (id == request) leaderboardsRequestStack.remove(index) leaderboardsRequestStack.append({fn = requestAction, id = id}) } } //-file:modified-container ================================================ FILE: testData/static_analyzer/w258_2.diag.txt ================================================ WARNING: w258 (similar-function) Function bodies are very similar. Consider functions 'updateEventLb' and 'updateEventLbSelfRow'. testData/static_analyzer/w258_2.nut:47:4 function updateEventLbSelfRow(requestData, id) { ^ local requestAction = (@(requestData, id) function () { ================================================ FILE: testData/static_analyzer/w258_2.nut ================================================ //expect:w257 //-file:undefined-global //-file:ident-hides-ident //-file:declared-never-used //-file:similar-assigned-expr //expect:w258 local canRequestEventLb = false let leaderboardsRequestStack = [] local class Aaa { function requestUpdateEventLb(x) {} canRequestEventLb = false leaderboardsRequestStack = [] function updateEventLb(requestData, id) { local requestAction = (@(requestData, id) function () { local taskId = ::requestUpdateEventLb(requestData) if (taskId < 0) return canRequestEventLb = false ::add_bg_task_cb(taskId, (@(requestData, id) function() { ::handleLbRequest(requestData, id) if (leaderboardsRequestStack.len()) ::array_shift(leaderboardsRequestStack).fn() else canRequestEventLb = true })(requestData, id).bindenv(this)) })(requestData, id).bindenv(this) if (canRequestEventLb) return requestAction() if (id) foreach (index, request in leaderboardsRequestStack) if (id == request) leaderboardsRequestStack.remove(index) leaderboardsRequestStack.append({fn = requestAction, id = id}) } function updateEventLbSelfRow(requestData, id) { local requestAction = (@(requestData, id) function () { local taskId = ::requestEventLbSelfRow(requestData) if (taskId < 0) return canRequestEventLb = false ::add_bg_task_cb(taskId, (@(requestData, id) function() { ::handleLbSelfRowRequest(requestData, id) if (leaderboardsRequestStack.len()) ::array_shift(leaderboardsRequestStack).fn() else canRequestEventLb = true })(requestData, id).bindenv(this)) })(requestData, id).bindenv(this) if (canRequestEventLb) return requestAction() if (id) foreach (index, request in leaderboardsRequestStack) if (id == request) leaderboardsRequestStack.remove(index) leaderboardsRequestStack.append({fn = requestAction, id = id}) } } //-file:modified-container ================================================ FILE: testData/static_analyzer/w259.diag.txt ================================================ WARNING: w259 (similar-assigned-expr) Assigned expression is very similar to one of the previous ones. testData/static_analyzer/w259.nut:23:26 local numAnimations = ::a + ::b + ::c + ::d - (::a + ::b + ::c + ::d) * ::x + 123 local numTextAnimations = ::a + ::b + ::c + ::d - (::a + ::b + ::c + ::d) * ::x + 124 // EXPECTED ^---------------------------------------------------------- ================================================ FILE: testData/static_analyzer/w259.nut ================================================ if (__name__ == "__analysis__") return //-file:undefined-global function Computed(_p) {} let au = {} const PP = "!1" const PM ="2" let _tt = Computed(@() au.value .findvalue(@(unlock) unlock?.name == PP)) let _ttp = Computed(@() au.value // FP 1 .findvalue(@(unlock) unlock?.name == PM)) function S(_p) {} let _P = @(...) S({p = vargv.len() > 1 ? vargv : vargv[0]}) let _M = @(...) S({m = vargv.len() > 1 ? vargv : vargv[0]}) // FP 2 local numAnimations = ::a + ::b + ::c + ::d - (::a + ::b + ::c + ::d) * ::x + 123 local numTextAnimations = ::a + ::b + ::c + ::d - (::a + ::b + ::c + ::d) * ::x + 124 // EXPECTED let _xx = [numAnimations, numTextAnimations] let D = {} let amin = {} let amax = {} let _gg = Computed(@() D.value > 0 ? ((D.value * 1000.0 - amin.value) * 0.1 / D.value).tointeger() : 0) let _ff = Computed(@() D.value > 0 ? ((D.value * 1000.0 - amax.value) * 0.1 / D.value).tointeger() : 0) // FP 3 ================================================ FILE: testData/static_analyzer/w260_local_function.diag.txt ================================================ WARNING: w260 (named-like-must-return-result) Function 'getSettings' has name like it should return a value, but not all control paths return a value. testData/static_analyzer/w260_local_function.nut:3:6 local function getSettings(path) { return } //-declared-never-used ^------------------------------------ ================================================ FILE: testData/static_analyzer/w260_local_function.nut ================================================ //expect:w260 local function getSettings(path) { return } //-declared-never-used return getSettings ================================================ FILE: testData/static_analyzer/w260_table.diag.txt ================================================ WARNING: w260 (named-like-must-return-result) Function 'get_setting_by_blk_path' has name like it should return a value, but not all control paths return a value. testData/static_analyzer/w260_table.nut:4:28 ::game <- { get_setting_by_blk_path = function(path) { return } //-declared-never-used ^------------------------ } ================================================ FILE: testData/static_analyzer/w260_table.nut ================================================ //expect:w260 ::game <- { get_setting_by_blk_path = function(path) { return } //-declared-never-used } ================================================ FILE: testData/static_analyzer/w260_table_sqconfig.diag.txt ================================================ WARNING: w260 (named-like-must-return-result) Function 'make_and_return_object' has name like it should return a value, but not all control paths return a value. testData/static_analyzer/w260_table_sqconfig.nut:4:27 ::game <- { make_and_return_object = function(x) { return } //-declared-never-used ^--------------------- } ================================================ FILE: testData/static_analyzer/w260_table_sqconfig.nut ================================================ //expect:w260 ::game <- { make_and_return_object = function(x) { return } //-declared-never-used } ================================================ FILE: testData/static_analyzer/w262.diag.txt ================================================ WARNING: w262 (suspicious-formatting) Suspicious code formatting. testData/static_analyzer/w262.nut:9:0 print(1) else // EXPECTED 1 ^--- print(2) WARNING: w262 (suspicious-formatting) Suspicious code formatting. testData/static_analyzer/w262.nut:14:4 print(3) print(4) // EXPECTED 2 ^---- WARNING: w262 (suspicious-formatting) Suspicious code formatting. testData/static_analyzer/w262.nut:18:4 print(x) print(y) // EXPECTED 3 ^---- WARNING: w262 (suspicious-formatting) Suspicious code formatting. testData/static_analyzer/w262.nut:26:4 print(x) print(y) // EXPECTED 4 ^---- WARNING: w262 (suspicious-formatting) Suspicious code formatting. testData/static_analyzer/w262.nut:30:4 print(x) print(y) // EXPECTED 5 ^---- WARNING: w262 (suspicious-formatting) Suspicious code formatting. testData/static_analyzer/w262.nut:39:4 print(x) print(y) // EXPECTED 6 ^---- WARNING: w262 (suspicious-formatting) Suspicious code formatting. testData/static_analyzer/w262.nut:25:0 while (false) print(x) ^------- print(y) // EXPECTED 4 WARNING: w262 (suspicious-formatting) Suspicious code formatting. testData/static_analyzer/w262.nut:58:0 if (x) // EXPECTED 7 if (y) ^ print(1) WARNING: w262 (suspicious-formatting) Suspicious code formatting. testData/static_analyzer/w262.nut:88:0 while (x > 100) print(3) // EXPECTED 8 ^------- WARNING: w262 (suspicious-formatting) Suspicious code formatting. testData/static_analyzer/w262.nut:92:0 for (;false;) print(3) // EXPECTED 9 ^------- ================================================ FILE: testData/static_analyzer/w262.nut ================================================ //-file:invalid-indentation local x = 10 local y = 20 if (x) if (y) print(1) else // EXPECTED 1 print(2) if (x) print(3) print(4) // EXPECTED 2 while (false) print(x) print(y) // EXPECTED 3 while (false) print(x) print(y) // FP 1 while (false) print(x) print(y) // EXPECTED 4 for (;false;) print(x) print(y) // EXPECTED 5 for (;false;) print(x) print(y) // FP 2 foreach (_z in []) print(x) print(y) // EXPECTED 6 foreach (_z in []) print(x) print(y) // FP 3 if (x) { print(10) } else // FP 4 print(20) if (x) { print(10) } else if (y) { // FP 5 print(20) } if (x) // EXPECTED 7 if (y) print(1) else print(2) function _f1() { print(1) print(2) } function _f2() { print(1) print(2) } if (x); // -empty-body print(3) while (x > 100); // -empty-body print(3) for (;false;); // -empty-body print(3) while (x > 100) print(3) // EXPECTED 8 for (;false;) print(3) // EXPECTED 9 local e = 10, d = 20, _z = null, aD = 2, eD = 3 if (e){ _z = { s = "e", ss = "p" } } else if (d > 0) { // FP 6 if (aD > eD) _z = { s = "a", ss = "p" } else _z = { s = "e", ss = "m" } } ================================================ FILE: testData/static_analyzer/w263.diag.txt ================================================ WARNING: w263 (egyptian-braces) Indentation style: 'egyptian braces' required. testData/static_analyzer/w263.nut:4:0 for (local i = 0; i < 5; i++) // EXPECTED { ^ print(i) WARNING: w263 (egyptian-braces) Indentation style: 'egyptian braces' required. testData/static_analyzer/w263.nut:9:0 function _foo(_p) // EXPECTED { ^ WARNING: w263 (egyptian-braces) Indentation style: 'egyptian braces' required. testData/static_analyzer/w263.nut:23:0 class _B // EXPECTED { ^ WARNING: w263 (egyptian-braces) Indentation style: 'egyptian braces' required. testData/static_analyzer/w263.nut:32:0 enum _E2 // EXPECTED { ^ WARNING: w263 (egyptian-braces) Indentation style: 'egyptian braces' required. testData/static_analyzer/w263.nut:43:0 try // EXPECTED { ^ print(8) WARNING: w263 (egyptian-braces) Indentation style: 'egyptian braces' required. testData/static_analyzer/w263.nut:46:0 } catch (_e) // EXPECTED { ^ print(9) WARNING: w263 (egyptian-braces) Indentation style: 'egyptian braces' required. testData/static_analyzer/w263.nut:59:0 if (!t) // EXPECTED { ^ print(12) WARNING: w263 (egyptian-braces) Indentation style: 'egyptian braces' required. testData/static_analyzer/w263.nut:62:0 } else // EXPECTED { ^ print(13) WARNING: w263 (egyptian-braces) Indentation style: 'egyptian braces' required. testData/static_analyzer/w263.nut:71:0 while (t) // EXPECTED { ^ print(15) WARNING: w263 (egyptian-braces) Indentation style: 'egyptian braces' required. testData/static_analyzer/w263.nut:80:0 do // EXPECTED { ^ print(17) WARNING: w263 (egyptian-braces) Indentation style: 'egyptian braces' required. testData/static_analyzer/w263.nut:91:4 default: // EXPECTED { ^ break; WARNING: w263 (egyptian-braces) Indentation style: 'egyptian braces' required. testData/static_analyzer/w263.nut:97:0 switch (t) // EXPECTED { ^ case 2: ================================================ FILE: testData/static_analyzer/w263.nut ================================================ #allow-switch-statement for (local i = 0; i < 5; i++) // EXPECTED { print(i) } function _foo(_p) // EXPECTED { } function bar() { // FP return false } class _A { // FP } class _B // EXPECTED { } enum _E1 { // FP } enum _E2 // EXPECTED { } try { // FP print(6) } catch (_e) { // FP print(7) } try // EXPECTED { print(8) } catch (_e) // EXPECTED { print(9) } local t = bar() if (t) { // FP print(10) } else { // FP print(11) } if (!t) // EXPECTED { print(12) } else // EXPECTED { print(13) } while (t) { // FP print(14) } while (t) // EXPECTED { print(15) } do { // FP print(16) } while (t) do // EXPECTED { print(17) } while (t) switch (t) { // FP case 1: break; case 2: { // FP break; } default: // EXPECTED { break; } } switch (t) // EXPECTED { case 2: break; default: break; } ================================================ FILE: testData/static_analyzer/w264.diag.txt ================================================ WARNING: w264 (plus-string) Usage of '+' for string concatenation. testData/static_analyzer/w264.nut:7:7 return a + 3 ^---- ================================================ FILE: testData/static_analyzer/w264.nut ================================================ //expect:w264 local a = "{0}".subst(null) if (a != "") return null return a + 3 ================================================ FILE: testData/static_analyzer/w266.diag.txt ================================================ WARNING: w266 (forgotten-do) 'while' after the statement list (forgot 'do' ?) testData/static_analyzer/w266.nut:10:2 ::x++ } while (::x) ^ ::x-- ================================================ FILE: testData/static_analyzer/w266.nut ================================================ if (__name__ == "__analysis__") return //-file:suspicious-formatting //-file:undefined-global //-file:invalid-indentation { ::x++ } while (::x) ::x-- ================================================ FILE: testData/static_analyzer/w267.diag.txt ================================================ WARNING: w267 (suspicious-bracket) '(' will be parsed as 'function call' (forgot ',' ?) testData/static_analyzer/w267.nut:11:7 foo(s1 (6+7)) // EXPECTED 1 ^ WARNING: w267 (suspicious-bracket) '[' will be parsed as 'access to member' (forgot ',' ?) testData/static_analyzer/w267.nut:13:7 foo(s2 [6]) // EXPECTED 2 access ^ WARNING: w267 (suspicious-bracket) '[' will be parsed as 'access to member' (forgot ',' ?) testData/static_analyzer/w267.nut:19:7 // [6] // compilation error s3 [7] // EXPECTED 3 access ^ s4 (6+7) // EXPECTED 4 WARNING: w267 (suspicious-bracket) '(' will be parsed as 'function call' (forgot ',' ?) testData/static_analyzer/w267.nut:20:7 s3 [7] // EXPECTED 3 access s4 (6+7) // EXPECTED 4 ^ "z" WARNING: w267 (suspicious-bracket) '(' will be parsed as 'function call' (forgot ',' ?) testData/static_analyzer/w267.nut:22:4 "z" (6+7) // EXPECTED 5 ^ ] ================================================ FILE: testData/static_analyzer/w267.nut ================================================ if (__name__ == "__analysis__") return // -file:paren-is-function-call function foo(...) {} let s1 = "sum=" let s2 = "array:" foo(s1 (6+7)) // EXPECTED 1 foo(s2 [6]) // EXPECTED 2 access let s3 = "t", s4 = "y" let _x = [ "x" // [6] // compilation error s3 [7] // EXPECTED 3 access s4 (6+7) // EXPECTED 4 "z" (6+7) // EXPECTED 5 ] foo(foo("b")) // FP ================================================ FILE: testData/static_analyzer/w269.diag.txt ================================================ WARNING: w269 (mixed-separators) Mixed spaces and commas to separate elements of array. testData/static_analyzer/w269.nut:4:6 20 30, ^ 40, WARNING: w269 (mixed-separators) Mixed spaces and commas to separate elements of array. testData/static_analyzer/w269.nut:13:4 30 40 ^- 50 ================================================ FILE: testData/static_analyzer/w269.nut ================================================ let _a = [ 10 20 30, 40, 50 ] let _b = [ 10, 20, 30 40 50 ] let _c = [ 10, 20, 30, 40, 50 ] let _d = [ 10 20 30 40 50 ] ================================================ FILE: testData/static_analyzer/w270.diag.txt ================================================ HINT: h270 (extent-to-append) It is better to use 'append(A, B, ...)' instead of 'extend([A, B, ...])'. testData/static_analyzer/w270.nut:6:0 ::handlersManager[::PERSISTENT_DATA_PARAMS].extend([ "curControlsAllowMask", "isCurSceneBgBlurred" ]) // -undefined-global ^---------------------------------------------------------------------------------------------------- ================================================ FILE: testData/static_analyzer/w270.nut ================================================ if (__name__ == "__analysis__") return //expect:w270 ::handlersManager[::PERSISTENT_DATA_PARAMS].extend([ "curControlsAllowMask", "isCurSceneBgBlurred" ]) // -undefined-global ================================================ FILE: testData/static_analyzer/w271.diag.txt ================================================ WARNING: w271 (forgot-subst) '{}' found inside string (forgot 'subst' or '$' ?). testData/static_analyzer/w271.nut:9:15 else return "$ xxxx={x}" // EXPECTED ^----------- } ================================================ FILE: testData/static_analyzer/w271.nut ================================================ //-file:declared-never-used function loc(...) {} function _foo(x) { if (x == 4) return $"$ yyyy={x}" // FP 1 else return "$ xxxx={x}" // EXPECTED } function _bar(a, b) { return "{a}/{b}".subst({a=a b=b}) // FP 2 } function _qux(a, b) { return loc("bb/aa", "{x} ({y})", {x=b y=b}) // FP 3 } function _bex(a, b) { return "jsadkl askdjal kjad".split("{css}") // FP 4 } ================================================ FILE: testData/static_analyzer/w272.diag.txt ================================================ WARNING: w272 (not-unary-op) This '-' is not a unary operator. Please use ' ' after it or ',' before it for better understandability. testData/static_analyzer/w272.nut:5:4 2 -3 // EXPECTED 1 ^ 4 WARNING: w272 (not-unary-op) This '+' is not a unary operator. Please use ' ' after it or ',' before it for better understandability. testData/static_analyzer/w272.nut:12:5 0 +1 // EXPECTED 2 ^ 2 ================================================ FILE: testData/static_analyzer/w272.nut ================================================ function foo(...) {} let _a = [ 0 2 -3 // EXPECTED 1 4 - 5 // FP 1 ] foo( 0 +1 // EXPECTED 2 2 + 3 // FP 2 4 * 5 // FP 3 14 /7 // FP 4 ) ================================================ FILE: testData/static_analyzer/w275.diag.txt ================================================ WARNING: w275 (missed-break) A 'break' statement is probably missing in a 'switch' statement. testData/static_analyzer/w275.nut:20:6 case 4: print("ccc") ^----------- default: // <<<<<<< here ================================================ FILE: testData/static_analyzer/w275.nut ================================================ #allow-switch-statement //expect:w275 function fn(x) { //-declared-never-used switch (x) { case 0: case 1: case 2: print("aaa") if (x == 1) { return } else { print("123") return } case 3: print("bbb") break case 4: print("ccc") default: // <<<<<<< here print("ddd") } } ================================================ FILE: testData/static_analyzer/w275_all_variants.diag.txt ================================================ WARNING: w275 (missed-break) A 'break' statement is probably missing in a 'switch' statement. testData/static_analyzer/w275_all_variants.nut:9:8 case 1: foo(); // OK ^---- case 2: { WARNING: w275 (missed-break) A 'break' statement is probably missing in a 'switch' statement. testData/static_analyzer/w275_all_variants.nut:20:8 case 3: foo() // OK ^---- default: ================================================ FILE: testData/static_analyzer/w275_all_variants.nut ================================================ #allow-switch-statement function foo() {} let x = foo() switch (x) { case 1: foo(); // OK case 2: { { foo(); { foo(); break; // WRONG } } } case 3: foo() // OK default: foo() break; // WRONG } switch (x) { case 2: foo(); break; // WRONG case 3: foo(); ; // WRONG -missed-break default: foo() // WRONG } switch (x) { case 1: //WRONG case 2: foo(); break; // WRONG case 3: foo() ; // WRONG -missed-break } switch (x) { case 1: foo() break // WRONG case 2: case 3: // WRONG } ================================================ FILE: testData/static_analyzer/w275_complex.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w275_complex.nut ================================================ if (__name__ == "__analysis__") return // No warnings here - the switch statement is large, but breaks are in every branch #allow-switch-statement function foo(_a, _b) {} function bar() {} let d = bar() let bgs = bar() let ab = bar() let sb = bar() local someBound = false switch (d.a() & d.M) { case d.D: { foreach (val in bgs) if (foo(10, val)) { someBound = true break } break } case d.A: { foreach (val in ab) { if (foo(val, 102)) { someBound = true break } if (foo(3, val.mn.devId) && foo(4, val.mx.devId)) { someBound = true break } } break } case d.T: { foreach (val in sb) { if (val.di == d.P || val.di == d.J || val.di == d.G || val.di == d.N) { someBound = true break } if (foo(4, val.mx.di) && foo(5, val.mx.di) && foo(7, val.my.di) && foo(8, val.my.di)) { someBound = true break } } break } } foo(4, someBound) ================================================ FILE: testData/static_analyzer/w277.diag.txt ================================================ WARNING: w277 (space-at-eol) Whitespace at the end of line. testData/static_analyzer/w277.nut:4:0 ^ //expect:w277 WARNING: w277 (space-at-eol) Whitespace at the end of line. testData/static_analyzer/w277.nut:6:0 //expect:w277 ^ //expect:w277 WARNING: w277 (space-at-eol) Whitespace at the end of line. testData/static_analyzer/w277.nut:7:13 //expect:w277 ^ WARNING: w277 (space-at-eol) Whitespace at the end of line. testData/static_analyzer/w277.nut:11:17 function _foo() { ^ return 10 WARNING: w277 (space-at-eol) Whitespace at the end of line. testData/static_analyzer/w277.nut:16:14 //expect:w277 let _z = @"abc ^ " WARNING: w277 (space-at-eol) Whitespace at the end of line. testData/static_analyzer/w277.nut:21:9 /* aas ^ */ WARNING: w277 (space-at-eol) Whitespace at the end of line. testData/static_analyzer/w277.nut:24:0 ^ ================================================ FILE: testData/static_analyzer/w277.nut ================================================ if (__name__ == "__analysis__") return //expect:w277 //expect:w277 function _foo() { return 10 } //expect:w277 let _z = @"abc " /* aas */ ================================================ FILE: testData/static_analyzer/w279_1.diag.txt ================================================ WARNING: w279 (mismatch-loop-variable) The variable used in for-loop does not match the initialized one. testData/static_analyzer/w279_1.nut:4:18 local j; for (local k = 0; j < 5; k++) { ^---- j = 10 ================================================ FILE: testData/static_analyzer/w279_1.nut ================================================ //-file:potentially-nulled-ops local j; for (local k = 0; j < 5; k++) { j = 10 } ================================================ FILE: testData/static_analyzer/w279_2.diag.txt ================================================ WARNING: w279 (mismatch-loop-variable) The variable used in for-loop does not match the initialized one. testData/static_analyzer/w279_2.nut:4:25 local j = 0; for (local k = 0; k < 5; j++) { ^-- k = 10 ================================================ FILE: testData/static_analyzer/w279_2.nut ================================================ //expect:w279 local j = 0; for (local k = 0; k < 5; j++) { k = 10 } ================================================ FILE: testData/static_analyzer/w280.diag.txt ================================================ WARNING: w280 (forbidden-parent-dir) Access to the parent directory is forbidden in this function. testData/static_analyzer/w280.nut:6:9 let _x = require_optional("../a.nut") ^--------------------------- ================================================ FILE: testData/static_analyzer/w280.nut ================================================ if (__name__ == "__analysis__") return //expect:w280 let _x = require_optional("../a.nut") ================================================ FILE: testData/static_analyzer/w281.diag.txt ================================================ WARNING: w281 (unwanted-modification) Function 'extend' modifies the object. You probably didn't want to modify the object here. testData/static_analyzer/w281.nut:13:9 function fn(arr) { return (arr ?? []).extend(x) ^-------------------- // ::y <- (arr ?? []).extend(x) WARNING: w281 (unwanted-modification) Function 'extend' modifies the object. You probably didn't want to modify the object here. testData/static_analyzer/w281.nut:18:12 local tab = { watch = (::alertWatched ? ::alertWatched : []).extend(::titleWatched ? ::titleWatched : []) ^---------------------------------------------------------------------------------- } WARNING: w281 (unwanted-modification) Function 'extend' modifies the object. You probably didn't want to modify the object here. testData/static_analyzer/w281.nut:21:36 local getSeasonMainPrizesData = @() (::premiumUnlock.value?.meta.promo ?? []) ^ .extend(::basicUnlock.value?.meta.promo ?? []) ================================================ FILE: testData/static_analyzer/w281.nut ================================================ if (__name__ == "__analysis__") return //expect:w281 //-file:declared-never-used //-file:undefined-global local x = [1, 2] local z = 555 function fn(arr) { return (arr ?? []).extend(x) // ::y <- (arr ?? []).extend(x) } local tab = { watch = (::alertWatched ? ::alertWatched : []).extend(::titleWatched ? ::titleWatched : []) } local getSeasonMainPrizesData = @() (::premiumUnlock.value?.meta.promo ?? []) .extend(::basicUnlock.value?.meta.promo ?? []) function baz(arr) { // both are temporaries - no warning needed return (x[0] ? [z] : []).extend(x) } ================================================ FILE: testData/static_analyzer/w283.diag.txt ================================================ WARNING: w283 (useless-null-coalescing) The expression to the right of the '??' is null. testData/static_analyzer/w283.nut:9:21 function fn(x) { return ::y.cc ?? x ?? null ^-------- } WARNING: w283 (useless-null-coalescing) The expression to the right of the '??' is null. testData/static_analyzer/w283.nut:13:10 local s = null local x = ::y ?? s ^------- return x ================================================ FILE: testData/static_analyzer/w283.nut ================================================ if (__name__ == "__analysis__") return //-file:declared-never-used //-file:undefined-global function fn(x) { return ::y.cc ?? x ?? null } local s = null local x = ::y ?? s return x ================================================ FILE: testData/static_analyzer/w284.diag.txt ================================================ WARNING: w284 (can-be-simplified) Expression can be simplified. testData/static_analyzer/w284.nut:10:10 let _c1 = ::a > 2 || ::a > 100 ^------------------- WARNING: w284 (can-be-simplified) Expression can be simplified. testData/static_analyzer/w284.nut:12:10 let _c2 = fn(1) != null ? fn(1) : null ^--------------------------- ================================================ FILE: testData/static_analyzer/w284.nut ================================================ if (__name__ == "__analysis__") return //-file:undefined-global function fn(x) { return ::y(x) } let _c1 = ::a > 2 || ::a > 100 let _c2 = fn(1) != null ? fn(1) : null ================================================ FILE: testData/static_analyzer/w285.diag.txt ================================================ WARNING: w285 (expr-cannot-be-null) The expression to the left of the 'null coalescing' cannot be null. testData/static_analyzer/w285.nut:9:5 local uu = ::sys.gh("fff") ?? "" if ((uu ?? "") != "") ^- print($"x: {uu}") WARNING: w285 (expr-cannot-be-null) The expression to the left of the 'null coalescing' cannot be null. testData/static_analyzer/w285.nut:13:42 local regions = ::unlock?.meta.regions ?? [::unlock?.meta.region] ?? [] ^---------------------- WARNING: w285 (expr-cannot-be-null) The expression to the left of the 'null coalescing' cannot be null. testData/static_analyzer/w285.nut:16:9 local regions2 = ::x ? [] : {} let _g = regions2 ?? 123 ^------- WARNING: w285 (expr-cannot-be-null) The expression to the left of the 'equal check' cannot be null. testData/static_analyzer/w285.nut:20:9 local regions3 = ::x ? 2 : 4 let _h = regions3 != null ^------- ================================================ FILE: testData/static_analyzer/w285.nut ================================================ if (__name__ == "__analysis__") return //-file:undefined-global //-file:declared-never-used local uu = ::sys.gh("fff") ?? "" if ((uu ?? "") != "") print($"x: {uu}") local regions = ::unlock?.meta.regions ?? [::unlock?.meta.region] ?? [] local regions2 = ::x ? [] : {} let _g = regions2 ?? 123 local regions3 = ::x ? 2 : 4 let _h = regions3 != null // This caused a false positive, but the bug was fixed function _foo(a=null){ let b = a a = a ?? 3 return [b ?? 2, a] } ================================================ FILE: testData/static_analyzer/w286.diag.txt ================================================ WARNING: w286 (decl-in-expression) Declaration used in arith expression as operand. testData/static_analyzer/w286.nut:5:7 return fn1 || ::fn2 // -undefined-global ^-- ================================================ FILE: testData/static_analyzer/w286.nut ================================================ //expect:w286 function fn1() {} return fn1 || ::fn2 // -undefined-global ================================================ FILE: testData/static_analyzer/w286_2.diag.txt ================================================ WARNING: w286 (decl-in-expression) Declaration used in arith expression as operand. testData/static_analyzer/w286_2.nut:10:7 return cls || ::fn2 //-const-in-bool-expr -undefined-global ^-- ================================================ FILE: testData/static_analyzer/w286_2.nut ================================================ //expect:w286 local cls = class { } return cls || ::fn2 //-const-in-bool-expr -undefined-global ================================================ FILE: testData/static_analyzer/w286_oror_andand.diag.txt ================================================ WARNING: w286 (decl-in-expression) Declaration used in arith expression as operand. testData/static_analyzer/w286_oror_andand.nut:7:9 let _p = t?.x || {} let _x = {} || t?.y ^- let _y = t?.z && [] WARNING: w286 (decl-in-expression) Declaration used in arith expression as operand. testData/static_analyzer/w286_oror_andand.nut:9:9 let _y = t?.z && [] let _z = [] && t?.g ^- ================================================ FILE: testData/static_analyzer/w286_oror_andand.nut ================================================ //-file:const-in-bool-expr //-file:expr-cannot-be-null let t = {} let _p = t?.x || {} let _x = {} || t?.y let _y = t?.z && [] let _z = [] && t?.g ================================================ FILE: testData/static_analyzer/w287.diag.txt ================================================ WARNING: w287 (range-check) It looks like the range boundaries are not checked correctly. Pay attention to checking with minimum and maximum index. testData/static_analyzer/w287.nut:10:10 let e1 = (curVal < 0 || curVal > x) // EXPECTED 1 ^----------------------- let c1 = (curVal < 0 || curVal >= x) // FP 1 WARNING: w287 (range-check) It looks like the range boundaries are not checked correctly. Pay attention to checking with minimum and maximum index. testData/static_analyzer/w287.nut:16:9 let e2 = (value >= 0) && (value <= cnt) // EXPECTED 2 ^---------------------------- let c2 = (value >= 0) && (value < cnt) // FP 2 WARNING: w287 (range-check) It looks like the range boundaries are not checked correctly. Pay attention to checking with minimum and maximum index. testData/static_analyzer/w287.nut:19:10 let e3 = (::idx < 0 || ::idx > ::tblObj.childrenCount()) // EXPECTED 3 ^-------------------------------------------- let c3 = (::idx < 0 || ::idx >= ::tblObj.childrenCount()) // FP 3 WARNING: w287 (range-check) It looks like the range boundaries are not checked correctly. Pay attention to checking with minimum and maximum index. testData/static_analyzer/w287.nut:22:10 let e4 = (0 <= value && value <= ::obj.childrenCount()) // EXPECTED 4 ^------------------------------------------- let c4 = (0 <= value && value < ::obj.childrenCount()) // FP 4 ================================================ FILE: testData/static_analyzer/w287.nut ================================================ if (__name__ == "__analysis__") return //-file:undefined-global //-file:declared-never-used local curVal = ::a local x = ::g_chat.rooms.len() let e1 = (curVal < 0 || curVal > x) // EXPECTED 1 let c1 = (curVal < 0 || curVal >= x) // FP 1 local value = ::a local cnt = ::listObj.childrenCount() let e2 = (value >= 0) && (value <= cnt) // EXPECTED 2 let c2 = (value >= 0) && (value < cnt) // FP 2 let e3 = (::idx < 0 || ::idx > ::tblObj.childrenCount()) // EXPECTED 3 let c3 = (::idx < 0 || ::idx >= ::tblObj.childrenCount()) // FP 3 let e4 = (0 <= value && value <= ::obj.childrenCount()) // EXPECTED 4 let c4 = (0 <= value && value < ::obj.childrenCount()) // FP 4 ================================================ FILE: testData/static_analyzer/w288.diag.txt ================================================ WARNING: w288 (param-count) Function '(w288.nut:4)' (2..2147483647 parameters) is called with the wrong number of arguments (1). testData/static_analyzer/w288.nut:7:7 local b = 1 return fn(b) ^---- HINT: h314 (see-other) You can find the function here. testData/static_analyzer/w288.nut:4:11 local fn = function (aaa_x, bbb, ...) {return aaa_x + bbb} ^---------------------------------------------- ================================================ FILE: testData/static_analyzer/w288.nut ================================================ if (__name__ == "__analysis__") return local fn = function (aaa_x, bbb, ...) {return aaa_x + bbb} local b = 1 return fn(b) ================================================ FILE: testData/static_analyzer/w288_dp_va.diag.txt ================================================ WARNING: w288 (param-count) Function 'foo' (1..3 parameters) is called with the wrong number of arguments (4). testData/static_analyzer/w288_dp_va.nut:15:0 foo(10, 20, 30, 40) ^------------------ foo(10, 20, 30) HINT: h314 (see-other) You can find the function here. testData/static_analyzer/w288_dp_va.nut:8:0 function foo(_x, _y = 10, _z = 20) {} ^------------------------------------ WARNING: w288 (param-count) Function 'foo' (1..3 parameters) is called with the wrong number of arguments (0). testData/static_analyzer/w288_dp_va.nut:19:0 foo(10) foo() ^---- HINT: h314 (see-other) You can find the function here. testData/static_analyzer/w288_dp_va.nut:8:0 function foo(_x, _y = 10, _z = 20) {} ^------------------------------------ WARNING: w288 (param-count) Function 'bar' (2..2147483647 parameters) is called with the wrong number of arguments (0). testData/static_analyzer/w288_dp_va.nut:22:0 bar() ^---- bar(10) HINT: h314 (see-other) You can find the function here. testData/static_analyzer/w288_dp_va.nut:10:0 function bar(_x, _y, ...) {} ^--------------------------- WARNING: w288 (param-count) Function 'bar' (2..2147483647 parameters) is called with the wrong number of arguments (1). testData/static_analyzer/w288_dp_va.nut:23:0 bar() bar(10) ^------ bar(10, 20) HINT: h314 (see-other) You can find the function here. testData/static_analyzer/w288_dp_va.nut:10:0 function bar(_x, _y, ...) {} ^--------------------------- ================================================ FILE: testData/static_analyzer/w288_dp_va.nut ================================================ if (__name__ == "__analysis__") return //-file:declared-never-used function comp(...) {} function foo(_x, _y = 10, _z = 20) {} function bar(_x, _y, ...) {} let FlowH = 30 let hflow = @(...) comp(FlowH, vargv) foo(10, 20, 30, 40) foo(10, 20, 30) foo(10, 20) foo(10) foo() bar() bar(10) bar(10, 20) bar(10, 20, 30) bar(10, 20, 30, 40) ================================================ FILE: testData/static_analyzer/w288_lambdas2.diag.txt ================================================ ================================================ FILE: testData/static_analyzer/w288_lambdas2.nut ================================================ for (local x = 0; x != 0; ++x) let _f = (@(val) @(arg) type(arg) == val)("sss") ================================================ FILE: testData/static_analyzer/w288_native.diag.txt ================================================ WARNING: w288 (param-count) Function 'floor' (1..1 parameters) is called with the wrong number of arguments (0). testData/static_analyzer/w288_native.nut:6:9 let _x = floor() ^------ let _y = floor(111.111, 222.222) HINT: h314 (see-other) You can find the function here. testData/static_analyzer/w288_native.nut:1:0 from "math" import floor, clamp ^ WARNING: w288 (param-count) Function 'floor' (1..1 parameters) is called with the wrong number of arguments (2). testData/static_analyzer/w288_native.nut:7:9 let _x = floor() let _y = floor(111.111, 222.222) ^---------------------- let _z = clamp(1, 2) HINT: h314 (see-other) You can find the function here. testData/static_analyzer/w288_native.nut:1:0 from "math" import floor, clamp ^ WARNING: w288 (param-count) Function 'clamp' (3..3 parameters) is called with the wrong number of arguments (2). testData/static_analyzer/w288_native.nut:8:9 let _y = floor(111.111, 222.222) let _z = clamp(1, 2) ^---------- HINT: h314 (see-other) You can find the function here. testData/static_analyzer/w288_native.nut:1:0 from "math" import floor, clamp ^ ================================================ FILE: testData/static_analyzer/w288_native.nut ================================================ from "math" import floor, clamp if (__name__ == "__analysis__") return let _x = floor() let _y = floor(111.111, 222.222) let _z = clamp(1, 2) ================================================ FILE: testData/static_analyzer/w288_require.diag.txt ================================================ WARNING: w288 (param-count) Function '(module_foo.nut:1)' (2..2 parameters) is called with the wrong number of arguments (1). testData/static_analyzer/w288_require.nut:2:20 let { foo } = require("testData/static_analyzer/module_foo.nut") return { bar = @(x) foo(x) } ^----- HINT: h314 (see-other) You can find the function here. testData/static_analyzer/w288_require.nut:1:6 let { foo } = require("testData/static_analyzer/module_foo.nut") ^-- return { bar = @(x) foo(x) } ================================================ FILE: testData/static_analyzer/w288_require.nut ================================================ let { foo } = require("testData/static_analyzer/module_foo.nut") return { bar = @(x) foo(x) } ================================================ FILE: testData/static_analyzer/w288_require_indirect.diag.txt ================================================ WARNING: w288 (param-count) Function '(module_foo.nut:1)' (2..2 parameters) is called with the wrong number of arguments (1). testData/static_analyzer/w288_require_indirect.nut:3:20 let foo = mod.foo return { bar = @(x) foo(x) } ^----- HINT: h314 (see-other) You can find the function here. testData/static_analyzer/w288_require_indirect.nut:2:10 let mod = require("testData/static_analyzer/module_foo.nut") let foo = mod.foo ^------ return { bar = @(x) foo(x) } ================================================ FILE: testData/static_analyzer/w288_require_indirect.nut ================================================ let mod = require("testData/static_analyzer/module_foo.nut") let foo = mod.foo return { bar = @(x) foo(x) } ================================================ FILE: testData/static_analyzer/w289.diag.txt ================================================ WARNING: w289 (param-pos) The function parameter 'aaax' seems to be in the wrong position. testData/static_analyzer/w289.nut:6:13 local b = -1; return fn(b, aaaX); ^--- ================================================ FILE: testData/static_analyzer/w289.nut ================================================ //expect:w289 function fn(aaa_x, bbb, ...) {return aaa_x + bbb} local aaaX = 1; local b = -1; return fn(b, aaaX); ================================================ FILE: testData/static_analyzer/w291.diag.txt ================================================ WARNING: w291 (invalid-underscore) The name of parameter '_y' is invalid. The identifier is marked as unused with a prefix underscore, but it is used. testData/static_analyzer/w291.nut:6:4 x, _y, // EXPECTED ^- _z // FP ================================================ FILE: testData/static_analyzer/w291.nut ================================================ function _foo( x, _y, // EXPECTED _z // FP ) { return x + _y; } ================================================ FILE: testData/static_analyzer/w292.diag.txt ================================================ WARNING: w292 (modified-container) The container was modified within the loop. testData/static_analyzer/w292.nut:14:4 foreach (a in c) { delete c.x // EXPECTED 1 ^--------- c.rawdelete("y") // EXPECTED 2 WARNING: w292 (modified-container) The container was modified within the loop. testData/static_analyzer/w292.nut:15:4 delete c.x // EXPECTED 1 c.rawdelete("y") // EXPECTED 2 ^--------------- } WARNING: w292 (modified-container) The container was modified within the loop. testData/static_analyzer/w292.nut:35:8 if (a < 0) { c.clear() // EXPECTED 3 ^-------- } else { ================================================ FILE: testData/static_analyzer/w292.nut ================================================ #allow-delete-operator //-file:declared-never-used //-file:unconditional-terminated-loop let c = {} foreach (a in c) { delete c.x // FP 1 c.rawdelete("y") // FP 2 break; } foreach (a in c) { delete c.x // EXPECTED 1 c.rawdelete("y") // EXPECTED 2 } function _f(arr) { foreach (a in arr) { c.remove(5) // FP 3 return 10; } return 20 } foreach (a in c) { if (a > 0) { c.remove(5) // FP 4 } return 10; } foreach (a in c) { if (a < 0) { c.clear() // EXPECTED 3 } else { throw "EX" } } ================================================ FILE: testData/static_analyzer/w293.diag.txt ================================================ WARNING: w293 (duplicate-persist-id) Duplicate id 'x' passed to 'persist'. testData/static_analyzer/w293.nut:5:17 let _y = persist("y", @() {}) // FP let _z = persist("x", @() {}) // EXPECTED ^-- WARNING: w293 (duplicate-persist-id) Duplicate id 'a' passed to 'persist'. testData/static_analyzer/w293.nut:14:28 let _c = mkWatched(foo, "b") // FP let _d = mkWatched(persist, "a") // EXPECTED ^-- ================================================ FILE: testData/static_analyzer/w293.nut ================================================ let _x = persist("x", @() {}) // FP let _y = persist("y", @() {}) // FP let _z = persist("x", @() {}) // EXPECTED function mkWatched(_f, _k) { } function foo(p) { return p } let _a = mkWatched(persist, "a") // FP let _b = mkWatched(persist, "b") // FP let _c = mkWatched(foo, "b") // FP let _d = mkWatched(persist, "a") // EXPECTED ================================================ FILE: testData/static_analyzer/w295.diag.txt ================================================ WARNING: w307 (global-id-redef) Redefinition of existing global name 'g'. testData/static_analyzer/w295.nut:7:0 WARNING: w307 (global-id-redef) Redefinition of existing global name 'g'. testData/static_analyzer/w295.nut:8:0 ================================================ FILE: testData/static_analyzer/w295.nut ================================================ if (__name__ == "__analysis__") return //-file:undefined-global ::f <- {} ::g <- 1 ::g <- 2 print(::h) ================================================ FILE: testData/static_analyzer/w297.diag.txt ================================================ WARNING: w297 (call-from-root) Function 'keepref' must be called from the root scope. testData/static_analyzer/w297.nut:9:4 function _foo(p) { keepref(p) // EXPECTED 1 ^--------- } WARNING: w297 (call-from-root) Function 'keepref' must be called from the root scope. testData/static_analyzer/w297.nut:13:14 let _z = @(y) keepref(y) // EXPECTED 2 ^--------- WARNING: w297 (call-from-root) Function 'keepref' must be called from the root scope. testData/static_analyzer/w297.nut:17:8 constructor(t){ keepref(t) // EXPECTED 3 ^--------- } ================================================ FILE: testData/static_analyzer/w297.nut ================================================ let x = {} keepref(x) // FP function _foo(p) { keepref(p) // EXPECTED 1 } let _z = @(y) keepref(y) // EXPECTED 2 class _C { constructor(t){ keepref(t) // EXPECTED 3 } } ================================================ FILE: testData/static_analyzer/w305.diag.txt ================================================ WARNING: w305 (relative-bool-cmp) Relative comparison of non-boolean with boolean. It is a potential runtime error. testData/static_analyzer/w305.nut:16:4 if ((B == x) > y) ^----------- print("a") ================================================ FILE: testData/static_analyzer/w305.nut ================================================ if (__name__ == "__analysis__") return const B = true local x = 1 local y = 1 let b1 = true let b2 = false print(b1 > b2) if (B == x > y) //-compared-with-bool print("a") if ((B == x) > y) print("a") ================================================ FILE: testData/static_analyzer/w306.diag.txt ================================================ WARNING: w306 (eq-paren-miss) Suspicious expression, probably parens are missing. testData/static_analyzer/w306.nut:6:4 local z = 2 if (x == y != z) ^---------- print("a") ================================================ FILE: testData/static_analyzer/w306.nut ================================================ //expect:w223 local x = 1 local y = 1 local z = 2 if (x == y != z) print("a") if ((x == y) != z) print("b") if (x == (y != z)) print("c") ================================================ FILE: testData/static_analyzer/w308.diag.txt ================================================ WARNING: w308 (bool-lambda-required) Function 'findindex' requires lambda which returns boolean. testData/static_analyzer/w308.nut:7:28 let _expected = o.findindex(@(t) t.id = 10) // EXPECTED ^------------- let _falsep = o.findvalue(@(t) t.id == 10) // FP 1 WARNING: w320 (callback-should-return-value) Callback passed to 'findindex' must return a value. testData/static_analyzer/w308.nut:7:28 let _expected = o.findindex(@(t) t.id = 10) // EXPECTED ^------------- let _falsep = o.findvalue(@(t) t.id == 10) // FP 1 ================================================ FILE: testData/static_analyzer/w308.nut ================================================ //-file:named-like-should-return let o = {} function startswith(_a, _b) { return false } let _expected = o.findindex(@(t) t.id = 10) // EXPECTED let _falsep = o.findvalue(@(t) t.id == 10) // FP 1 let _sr = o.findvalue(@(s) s.s == 10 && (s?.e ?? 0) > 0 ) // FP 2 o.findvalue(@(p) o?[p] ?? (p == "000")) // FP 3 let _set = o.filter(@(a) a && startswith(a, "9")) // FP 4 ================================================ FILE: testData/static_analyzer/w309_require.diag.txt ================================================ WARNING: w309 (missing-destructured-value) No value for 'bar' in destructured declaration. testData/static_analyzer/w309_require.nut:4:10 let { foo bar } = require("testData/static_analyzer/module_foo.nut") ^-- ================================================ FILE: testData/static_analyzer/w309_require.nut ================================================ if (__name__ == "__analysis__") return let { foo bar } = require("testData/static_analyzer/module_foo.nut") return { foo bar } ================================================ FILE: testData/static_analyzer/w310_require.diag.txt ================================================ WARNING: w310 (destructured-type) Destructured type mismatch, right side is table. testData/static_analyzer/w310_require.nut:4:0 let [ foo bar ] = require("testData/static_analyzer/module_foo.nut") ^------------------------------------------------------------------- ================================================ FILE: testData/static_analyzer/w310_require.nut ================================================ if (__name__ == "__analysis__") return let [ foo bar ] = require("testData/static_analyzer/module_foo.nut") return { foo, bar } ================================================ FILE: testData/static_analyzer/w312_require.diag.txt ================================================ WARNING: w312 (missing-field) No field 'baz' in table. testData/static_analyzer/w312_require.nut:4:10 let baz = require("testData/static_analyzer/module_foo.nut").baz ^----------------------------------------------------- return baz(1) HINT: h314 (see-other) You can find source of table here. testData/static_analyzer/w312_require.nut:4:10 let baz = require("testData/static_analyzer/module_foo.nut").baz ^------------------------------------------------- return baz(1) ================================================ FILE: testData/static_analyzer/w312_require.nut ================================================ if (__name__ == "__analysis__") return let baz = require("testData/static_analyzer/module_foo.nut").baz return baz(1) ================================================ FILE: testData/static_analyzer/w318_merge_empty_table.diag.txt ================================================ WARNING: w318 (merge-empty-table) '__merge({})' with an empty table is equivalent to 'clone'. Use 'clone' instead. testData/static_analyzer/w318_merge_empty_table.nut:5:8 let a = t.__merge({}) //warning:merge-empty-table ^------------ ================================================ FILE: testData/static_analyzer/w318_merge_empty_table.nut ================================================ //-file:declared-never-used let t = {a = 1, b = 2} let a = t.__merge({}) //warning:merge-empty-table let b = t.__merge({x = 1}) // ok, non-empty table let c = clone t // ok, already using clone ================================================ FILE: testData/static_analyzer/w319_empty_array_resize.diag.txt ================================================ WARNING: w319 (empty-array-resize) '[].resize(...)' is slower than 'array(...)'. Use 'array(...)' instead. testData/static_analyzer/w319_empty_array_resize.nut:3:8 let a = [].resize(10, 0) //warning:empty-array-resize ^--------------- ================================================ FILE: testData/static_analyzer/w319_empty_array_resize.nut ================================================ //-file:declared-never-used let a = [].resize(10, 0) //warning:empty-array-resize let b = array(10, 0) // ok, already using array() let c = [1, 2, 3].resize(10, 0) // ok, non-empty array ================================================ FILE: testData/static_analyzer/w320.diag.txt ================================================ WARNING: w320 (callback-should-return-value) Callback passed to 'filter' must return a value. testData/static_analyzer/w320.nut:17:23 // filter without return let _f1 = arr.filter(function(v) { print(v) }) ^----------------------- WARNING: w320 (callback-should-return-value) Callback passed to 'reduce' must return a value. testData/static_analyzer/w320.nut:20:23 // reduce without return let _r1 = arr.reduce(function(acc, v) { print(acc + v) }) ^---------------------------------- WARNING: w320 (callback-should-return-value) Callback passed to 'findindex' must return a value. testData/static_analyzer/w320.nut:23:27 // findindex without return let _fi1 = arr.findindex(function(v) { print(v) }) ^----------------------- WARNING: w320 (callback-should-return-value) Callback passed to 'findvalue' must return a value. testData/static_analyzer/w320.nut:26:27 // findvalue without return let _fv1 = arr.findvalue(function(v) { print(v) }) ^----------------------- WARNING: w320 (callback-should-return-value) Callback passed to 'sort' must return a value. testData/static_analyzer/w320.nut:29:11 // sort without return arr.sort(function(a, b) { print(a + b) }) ^------------------------------ WARNING: w320 (callback-should-return-value) Callback passed to 'apply' must return a value. testData/static_analyzer/w320.nut:32:12 // apply without return arr.apply(function(v) { print(v) }) ^----------------------- WARNING: w320 (callback-should-return-value) Callback passed to 'map' must return a value. testData/static_analyzer/w320.nut:35:21 // table map without return let _tm1 = tbl.map(function(v) { print(v) }) ^----------------------- WARNING: w320 (callback-should-return-value) Callback passed to 'filter' must return a value. testData/static_analyzer/w320.nut:38:24 // table filter without return let _tf1 = tbl.filter(function(v) { print(v) }) ^----------------------- WARNING: w320 (callback-should-return-value) Callback passed to 'map' must return a value. testData/static_analyzer/w320.nut:41:20 // partial return (not all paths) let _m2 = arr.map(function(v) { ^ if (v > 0) WARNING: w320 (callback-should-return-value) Callback passed to 'map' must return a value. testData/static_analyzer/w320.nut:47:19 // function passed as binding let noReturnFn = function(v) { print(v) } ^----------------------- let _m5 = arr.map(noReturnFn) WARNING: w320 (callback-should-return-value) Callback passed to 'filter' must return a value. testData/static_analyzer/w320.nut:51:18 // binding with partial return let partialFn = function(v) { ^ if (v > 0) ================================================ FILE: testData/static_analyzer/w320.nut ================================================ //-file:declared-never-used //-file:named-like-should-return //-file:all-paths-return-value //-file:result-not-utilized //-file:bool-lambda-required function test_warnings() { let arr = [1, 2, 3] let tbl = {a = 1, b = 2} // ===== SHOULD WARN ===== // map without return let _m1 = arr.map(@(v) print(v)) // filter without return let _f1 = arr.filter(function(v) { print(v) }) // reduce without return let _r1 = arr.reduce(function(acc, v) { print(acc + v) }) // findindex without return let _fi1 = arr.findindex(function(v) { print(v) }) // findvalue without return let _fv1 = arr.findvalue(function(v) { print(v) }) // sort without return arr.sort(function(a, b) { print(a + b) }) // apply without return arr.apply(function(v) { print(v) }) // table map without return let _tm1 = tbl.map(function(v) { print(v) }) // table filter without return let _tf1 = tbl.filter(function(v) { print(v) }) // partial return (not all paths) let _m2 = arr.map(function(v) { if (v > 0) return v * 2 }) // function passed as binding let noReturnFn = function(v) { print(v) } let _m5 = arr.map(noReturnFn) // binding with partial return let partialFn = function(v) { if (v > 0) return v * 2 } let _f5 = arr.filter(partialFn) // ===== SHOULD NOT WARN ===== // map with return let _m3 = arr.map(@(v) v * 2) // filter with return let _f3 = arr.filter(@(v) v > 1) // reduce with return let _r3 = arr.reduce(@(acc, v) acc + v) // findindex with return let _fi3 = arr.findindex(@(v) v == 2) // findvalue with return let _fv3 = arr.findvalue(@(v) v == 2) // sort with return arr.sort(@(a, b) a <=> b) // apply with return arr.apply(@(v) v * 2) // block body with return on all paths let _m4 = arr.map(function(v) { if (v > 0) return v * 2 else return v }) // each (not in the list, should not warn) arr.each(@(v) print(v)) // binding with proper return let goodFn = @(v) v * 2 let _m6 = arr.map(goodFn) } ================================================ FILE: testData/static_analyzer/w321.diag.txt ================================================ WARNING: w321 (param-assign-in-lambda) Assignment to parameter 'a' in lambda has no effect. Return the expression instead. testData/static_analyzer/w321.nut:17:29 // reduce with assignment to accumulator let _r1 = arr.reduce(@(a, v) a = max(v, a), 0) ^------------ WARNING: w321 (param-assign-in-lambda) Assignment to parameter 'v' in lambda has no effect. Return the expression instead. testData/static_analyzer/w321.nut:20:23 // map with assignment to parameter let _m1 = arr.map(@(v) v = v * 2) ^-------- WARNING: w321 (param-assign-in-lambda) Assignment to parameter 'v' in lambda has no effect. Return the expression instead. testData/static_analyzer/w321.nut:23:26 // filter with assignment let _f1 = arr.filter(@(v) v = v > 1) ^-------- WARNING: w321 (param-assign-in-lambda) Assignment to parameter 'a' in lambda has no effect. Return the expression instead. testData/static_analyzer/w321.nut:26:33 // table reduce with assignment let _r3 = tbl.reduce(@(a, v, _k) a = a + v, 0) ^-------- ================================================ FILE: testData/static_analyzer/w321.nut ================================================ //-file:declared-never-used //-file:named-like-should-return //-file:all-paths-return-value //-file:result-not-utilized //-file:bool-lambda-required //-file:callback-should-return-value //-file:param-pos let arr = [1, 2, 3] let tbl = {a = 1, b = 2} function max(a, b) { return a > b ? a : b } // ===== SHOULD WARN ===== // reduce with assignment to accumulator let _r1 = arr.reduce(@(a, v) a = max(v, a), 0) // map with assignment to parameter let _m1 = arr.map(@(v) v = v * 2) // filter with assignment let _f1 = arr.filter(@(v) v = v > 1) // table reduce with assignment let _r3 = tbl.reduce(@(a, v, _k) a = a + v, 0) // ===== SHOULD NOT WARN ===== // block-body function: assignment to param then use is ok let _r2 = arr.reduce(function(a, v) { a = a + v; return a }, 0) // proper reduce (returns expression) let _r4 = arr.reduce(@(a, v) max(v, a), 0) // proper map let _m4 = arr.map(@(v) v * 2) // assignment to local, not parameter let _m5 = arr.map(function(v) { local res = v * 2 return res }) // assignment to parameter in regular named function (not lambda) function process(x) { x = x * 2 return x } // assignment to outer variable, not parameter local accumulator = 0 arr.each(function(v) { accumulator = accumulator + v }) ================================================ FILE: testData/static_analyzer/w322.diag.txt ================================================ WARNING: w322 (callback-should-not-return) Callback passed to 'each' should not return a value. testData/static_analyzer/w322.nut:10:9 // each with explicit return value arr.each(@(v) v * 2) ^--------- WARNING: w322 (callback-should-not-return) Callback passed to 'each' should not return a value. testData/static_analyzer/w322.nut:13:9 // each with block body returning value arr.each(function(v) { return v * 2 }) ^--------------------------- WARNING: w322 (callback-should-not-return) Callback passed to 'each' should not return a value. testData/static_analyzer/w322.nut:16:9 // table each with return tbl.each(@(v) v + 1) ^--------- WARNING: w322 (callback-should-not-return) Callback passed to 'each' should not return a value. testData/static_analyzer/w322.nut:19:18 // each with binding that returns let returningFn = function(v) { return v * 2 } ^--------------------------- arr.each(returningFn) ================================================ FILE: testData/static_analyzer/w322.nut ================================================ //-file:declared-never-used //-file:result-not-utilized let arr = [1, 2, 3] let tbl = {a = 1, b = 2} // ===== SHOULD WARN ===== // each with explicit return value arr.each(@(v) v * 2) // each with block body returning value arr.each(function(v) { return v * 2 }) // table each with return tbl.each(@(v) v + 1) // each with binding that returns let returningFn = function(v) { return v * 2 } arr.each(returningFn) // ===== SHOULD NOT WARN ===== // each with side-effect only (no return value) arr.each(function(v) { print(v) }) // each with lambda calling void function arr.each(@(v) print(v)) // lambda with single call expression (side-effect intent) local tmp = [] arr.each(@(v) tmp.append(v)) // mutate with side-effect only //Watched.mutate(function(v) { v.append(1) }) // each with block body, no return tbl.each(function(_k, _v) { print("ok") }) ================================================ FILE: testData/types/default_values.nut.txt ================================================ fn(a = 111) fn(a: bool = true) fn(x, a = 111) fn(x, a: bool = true) fn(x: bool, a = 111) fn(x: bool, a: bool = true) fn(x: bool, a = 111) fn(tab: table = {}) fn(tab: table = {x = 5, y = 6}) fn(arr: array = []) fn(arr: array = [234, null, value]) fn(expr: int = (0)) fn(expr: int = (20 + 50 * 10)) fn(c: int = Color32(123, 45, 45, 23)) fn(c: string = "{") fn(c: int = '{') fn(c: string = "]") fn(c: string = "\"") fn(c: int = '\'') fn(c: int = '"') fn(c: string = "") fn(c1: int = Color32(123, 45, 45, 23), c2: int = Color32(33, 44, 55, 66)) fn([a = 111]) fn([a: bool = true]) fn([x, a = 111]) fn([x, a: bool = true]) fn([x: bool, a = 111]) fn([x: bool, a: bool = true]) fn([x: bool, a = 111]) fn([tab: table = {}]) fn([tab: table = {x = 5, y = 6}]) fn([arr: array = []]) fn([arr: array = [234, null, value]]) fn([expr: int = (0)]) fn([expr: int = (20 + 50 * 10)]) fn([c: int = Color32(123, 45, 45, 23)]) fn([c1: int = Color32(123, 45, 45, 23), c2: int = Color32(33, 44, 55, 66)]) fn([c: string = "{"]) fn([c: int = '{']) ================================================ FILE: testData/types/default_values.out ================================================ fn(a = 111) fn([a = 111]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn(a: bool = true) fn([a: bool = true]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn(x, a = 111) fn(x, [a = 111]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 1 argCount: 2 pure: false nodiscard: false fn(x, a: bool = true) fn(x, [a: bool = true]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 1 argCount: 2 pure: false nodiscard: false fn(x: bool, a = 111) fn(x: bool, [a = 111]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 1 argCount: 2 pure: false nodiscard: false fn(x: bool, a: bool = true) fn(x: bool, [a: bool = true]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 1 argCount: 2 pure: false nodiscard: false fn(x: bool, a = 111) fn(x: bool, [a = 111]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 1 argCount: 2 pure: false nodiscard: false fn(tab: table = {}) fn([tab: table = {}]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn(tab: table = {x = 5, y = 6}) fn([tab: table = {x = 5, y = 6}]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn(arr: array = []) fn([arr: array = []]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn(arr: array = [234, null, value]) fn([arr: array = [234, null, value]]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn(expr: int = (0)) fn([expr: int = (0)]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn(expr: int = (20 + 50 * 10)) fn([expr: int = (20 + 50 * 10)]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn(c: int = Color32(123, 45, 45, 23)) fn([c: int = Color32(123, 45, 45, 23)]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn(c: string = "{") fn([c: string = "{"]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn(c: int = '{') fn([c: int = '{']) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn(c: string = "]") fn([c: string = "]"]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn(c: string = "\"") fn([c: string = "\""]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn(c: int = '\'') fn([c: int = '\'']) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn(c: int = '"') fn([c: int = '"']) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn(c: string = "") fn([c: string = ""]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn(c1: int = Color32(123, 45, 45, 23), c2: int = Color32(33, 44, 55, 66)) fn([c1: int = Color32(123, 45, 45, 23), c2: int = Color32(33, 44, 55, 66)]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 2 pure: false nodiscard: false fn([a = 111]) fn([a = 111]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn([a: bool = true]) fn([a: bool = true]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn([x, a = 111]) fn([x, a = 111]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 2 pure: false nodiscard: false fn([x, a: bool = true]) fn([x, a: bool = true]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 2 pure: false nodiscard: false fn([x: bool, a = 111]) fn([x: bool, a = 111]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 2 pure: false nodiscard: false fn([x: bool, a: bool = true]) fn([x: bool, a: bool = true]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 2 pure: false nodiscard: false fn([x: bool, a = 111]) fn([x: bool, a = 111]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 2 pure: false nodiscard: false fn([tab: table = {}]) fn([tab: table = {}]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn([tab: table = {x = 5, y = 6}]) fn([tab: table = {x = 5, y = 6}]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn([arr: array = []]) fn([arr: array = []]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn([arr: array = [234, null, value]]) fn([arr: array = [234, null, value]]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn([expr: int = (0)]) fn([expr: int = (0)]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn([expr: int = (20 + 50 * 10)]) fn([expr: int = (20 + 50 * 10)]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn([c: int = Color32(123, 45, 45, 23)]) fn([c: int = Color32(123, 45, 45, 23)]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn([c1: int = Color32(123, 45, 45, 23), c2: int = Color32(33, 44, 55, 66)]) fn([c1: int = Color32(123, 45, 45, 23), c2: int = Color32(33, 44, 55, 66)]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 2 pure: false nodiscard: false fn([c: string = "{"]) fn([c: string = "{"]) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false fn([c: int = '{']) fn([c: int = '{']) functionName: fn returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 1 pure: false nodiscard: false ================================================ FILE: testData/types/default_values_invalid.nut.txt ================================================ fn(a =, 111) fn(a: bool = [true) fn(x, a = 111}) fn(x, a: bool = ") fn(x: bool, a =( 111) fn(x: bool, a: bool, = true) fn(x: bool, a = 1}11) fn(tab: table = {[}) fn(tab: table = {x = 5), y = 6}) fn(arr: array [= []) fn(arr: array = [234, null, {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{value]) fn(expr: int = ((((0)))) fn(expr: int = ,(20 + 50 * 10)) fn(c: int = Color32(123, 45, 45, 23\"),) fn(c: int = '{\') fn(c: int = '") fn(c: int = "\') fn(c1: int := Color32(123, 45, 45, 23), c2: int = Color32(33, 44, 55, 66)) fn([a = 1,11]) fn([a: bool = [true]) fn([x, a = 111}]) fn([x, a: bool = true][) fn([x: bool, a = 111][]) fn([x: bool, a: bool = [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[true]) fn([x: bool, a = [111]) fn([tab: table = {]}]) fn([tab: table := {x = 5, y = 6}]) fn([arr: array = [{]]) fn([arr: array = [234, null, value]] = null) fn([expr: int = ](0)]) fn([expr: int = (20 + 50 * 10)}]) fn([c: int = Color32({123, 45, 45, 23)]) fn([c1: int = Color32(123, 45, 45, 23), c2: int = Color32,(33, 44, 55, 66)]) ================================================ FILE: testData/types/default_values_invalid.out ================================================ fn(a =, 111) ERROR: Expected default value after '=' at testData/types/default_values_invalid.nut.txt:1:7 fn(a: bool = [true) ERROR: Unmatched ')' in default value at testData/types/default_values_invalid.nut.txt:2:19 fn(x, a = 111}) ERROR: Unmatched '}' in default value at testData/types/default_values_invalid.nut.txt:3:14 fn(x, a: bool = ") ERROR: Unterminated function type string at testData/types/default_values_invalid.nut.txt:4:19 fn(x: bool, a =( 111) ERROR: Unterminated function type string at testData/types/default_values_invalid.nut.txt:5:22 fn(x: bool, a: bool, = true) ERROR: Expected argument name at testData/types/default_values_invalid.nut.txt:6:22 fn(x: bool, a = 1}11) ERROR: Unmatched '}' in default value at testData/types/default_values_invalid.nut.txt:7:18 fn(tab: table = {[}) ERROR: Unmatched '}' in default value at testData/types/default_values_invalid.nut.txt:8:19 fn(tab: table = {x = 5), y = 6}) ERROR: Unmatched ')' in default value at testData/types/default_values_invalid.nut.txt:9:23 fn(arr: array [= []) ERROR: Expected argument name at testData/types/default_values_invalid.nut.txt:10:16 fn(arr: array = [234, null, {{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{{value]) ERROR: Default value too complex. Too many nested structures at testData/types/default_values_invalid.nut.txt:11:67 fn(expr: int = ((((0)))) ERROR: Unterminated function type string at testData/types/default_values_invalid.nut.txt:12:25 fn(expr: int = ,(20 + 50 * 10)) ERROR: Expected default value after '=' at testData/types/default_values_invalid.nut.txt:13:16 fn(c: int = Color32(123, 45, 45, 23\"),) ERROR: Unfinished default value, unmatched brackets at testData/types/default_values_invalid.nut.txt:14:41 fn(c: int = '{\') ERROR: Unterminated function type string at testData/types/default_values_invalid.nut.txt:15:18 fn(c: int = '") ERROR: Unterminated function type string at testData/types/default_values_invalid.nut.txt:16:16 fn(c: int = "\') ERROR: Unterminated function type string at testData/types/default_values_invalid.nut.txt:17:17 fn(c1: int := Color32(123, 45, 45, 23), c2: int = Color32(33, 44, 55, 66)) ERROR: Expected ',' or ')' at testData/types/default_values_invalid.nut.txt:18:12 fn([a = 1,11]) ERROR: Expected argument name at testData/types/default_values_invalid.nut.txt:19:11 fn([a: bool = [true]) ERROR: Unmatched '[' at testData/types/default_values_invalid.nut.txt:20:21 fn([x, a = 111}]) ERROR: Unmatched '}' in default value at testData/types/default_values_invalid.nut.txt:21:15 fn([x, a: bool = true][) ERROR: Optional block must be the last argument at testData/types/default_values_invalid.nut.txt:22:23 fn([x: bool, a = 111][]) ERROR: Optional block must be the last argument at testData/types/default_values_invalid.nut.txt:23:22 fn([x: bool, a: bool = [[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[[true]) ERROR: Default value too complex. Too many nested structures at testData/types/default_values_invalid.nut.txt:24:63 fn([x: bool, a = [111]) ERROR: Unmatched '[' at testData/types/default_values_invalid.nut.txt:25:23 fn([tab: table = {]}]) ERROR: Unmatched ']' in default value at testData/types/default_values_invalid.nut.txt:26:19 fn([tab: table := {x = 5, y = 6}]) ERROR: Expected ',' or ')' at testData/types/default_values_invalid.nut.txt:27:16 fn([arr: array = [{]]) ERROR: Unmatched ']' in default value at testData/types/default_values_invalid.nut.txt:28:20 fn([arr: array = [234, null, value]] = null) ERROR: Argument after optional block at testData/types/default_values_invalid.nut.txt:29:38 fn([expr: int = ](0)]) ERROR: Expected default value after '=' at testData/types/default_values_invalid.nut.txt:30:17 fn([expr: int = (20 + 50 * 10)}]) ERROR: Unmatched '}' in default value at testData/types/default_values_invalid.nut.txt:31:31 fn([c: int = Color32({123, 45, 45, 23)]) ERROR: Unmatched ')' in default value at testData/types/default_values_invalid.nut.txt:32:38 fn([c1: int = Color32(123, 45, 45, 23), c2: int = Color32,(33, 44, 55, 66)]) ERROR: Expected argument name at testData/types/default_values_invalid.nut.txt:33:59 ================================================ FILE: testData/types/invalid.nut.txt ================================================ x 1 ( ) [ ] ... int - , . | (| (int function_name(()) function_name([) function_name(]) function_name(.) function_name(,,,,,,,,x) function_name(x x x) 1function_name(arg1_name: number, [arg2_name: float, arg3_name: float], ...) function_name)(arg1_name: number, [arg2_name: float, arg3_name: float], ...) function_name.(arg1_name: number, [arg2_name: float, arg3_name: float], ...) function_name(1arg1_name: number, [arg2_name: float, arg3_name: float], ...) function_name(arg1_name: 1number, [arg2_name: float, arg3_name: float], ...) function_name(arg1_name: number, )[arg2_name: float, arg3_name: float], ...) function_name(arg1_name: number, [[arg2_name: float, arg3_name: float], ...) function_name(arg1_name: number, ][arg2_name: float, arg3_name: float], ...) function_name(arg1_name: number, [arg2_name float, arg3_name: float], ...) function_name(arg1_name: number, [arg2_name: float arg3_name: float], ...) function_name(arg1_name: number, [arg2_name: float, arg3_name: float, ...) function_name(arg1_name: number, [arg2_name: float, arg3_name: float] ....) function_name(arg1_name: number, [arg2_name: float, arg3_name: float], ...]) function_name(arg1_name: number, [arg2_name: float, arg3_name: float],] ...) function_name(arg1_name: number, [arg2_name: float, arg3_name: float], [...]) function_name(arg1_name: number, [arg2_name: float, arg3_name: float], ... function_name(arg1_name: number, [arg2_name: float, arg3_name: float], ...( pure_ function_name() nodiscard_ function_name() pure nodiscard_ function_name() ================================================ FILE: testData/types/invalid.out ================================================ x ERROR: Expected '(' after function name at testData/types/invalid.nut.txt:1:2 1 ERROR: Expected function name at testData/types/invalid.nut.txt:2:1 ( ERROR: Expected type name at testData/types/invalid.nut.txt:3:2 ) ERROR: Expected function name at testData/types/invalid.nut.txt:4:1 [ ERROR: Expected function name at testData/types/invalid.nut.txt:5:1 ] ERROR: Expected function name at testData/types/invalid.nut.txt:6:1 ... ERROR: Expected function name at testData/types/invalid.nut.txt:7:1 int ERROR: Expected '(' after function name at testData/types/invalid.nut.txt:8:4 - ERROR: Expected function name at testData/types/invalid.nut.txt:9:1 , ERROR: Expected function name at testData/types/invalid.nut.txt:10:1 . ERROR: Expected function name at testData/types/invalid.nut.txt:11:1 | ERROR: Expected function name at testData/types/invalid.nut.txt:12:1 (| ERROR: Expected type name at testData/types/invalid.nut.txt:13:2 (int ERROR: Expected ')' after type list at testData/types/invalid.nut.txt:14:5 function_name(()) ERROR: Expected argument name at testData/types/invalid.nut.txt:15:15 function_name([) ERROR: Unmatched '[' at testData/types/invalid.nut.txt:16:16 function_name(]) ERROR: Unmatched ']' at testData/types/invalid.nut.txt:17:15 function_name(.) ERROR: Expected argument name at testData/types/invalid.nut.txt:18:15 function_name(,,,,,,,,x) ERROR: Argument expected before ',' at testData/types/invalid.nut.txt:19:15 function_name(x x x) ERROR: Expected ':' after argument name at testData/types/invalid.nut.txt:20:17 1function_name(arg1_name: number, [arg2_name: float, arg3_name: float], ...) ERROR: Expected function name at testData/types/invalid.nut.txt:21:1 function_name)(arg1_name: number, [arg2_name: float, arg3_name: float], ...) ERROR: Expected '(' after function name at testData/types/invalid.nut.txt:22:14 function_name.(arg1_name: number, [arg2_name: float, arg3_name: float], ...) ERROR: Expected function name after '.' at testData/types/invalid.nut.txt:23:15 function_name(1arg1_name: number, [arg2_name: float, arg3_name: float], ...) ERROR: Expected argument name at testData/types/invalid.nut.txt:24:15 function_name(arg1_name: 1number, [arg2_name: float, arg3_name: float], ...) ERROR: Expected type name at testData/types/invalid.nut.txt:25:26 function_name(arg1_name: number, )[arg2_name: float, arg3_name: float], ...) ERROR: Unexpected characters after function type string at testData/types/invalid.nut.txt:26:35 function_name(arg1_name: number, [[arg2_name: float, arg3_name: float], ...) ERROR: Nested optional blocks are not allowed at testData/types/invalid.nut.txt:27:35 function_name(arg1_name: number, ][arg2_name: float, arg3_name: float], ...) ERROR: Unmatched ']' at testData/types/invalid.nut.txt:28:34 function_name(arg1_name: number, [arg2_name float, arg3_name: float], ...) ERROR: Expected ':' after argument name at testData/types/invalid.nut.txt:29:45 function_name(arg1_name: number, [arg2_name: float arg3_name: float], ...) ERROR: Expected ',' or ')' at testData/types/invalid.nut.txt:30:52 function_name(arg1_name: number, [arg2_name: float, arg3_name: float, ...) ERROR: Unmatched '[' at testData/types/invalid.nut.txt:31:74 function_name(arg1_name: number, [arg2_name: float, arg3_name: float] ....) ERROR: Expected ')' after ellipsis argument at testData/types/invalid.nut.txt:32:74 function_name(arg1_name: number, [arg2_name: float, arg3_name: float], ...]) ERROR: Unmatched ']' at testData/types/invalid.nut.txt:33:75 function_name(arg1_name: number, [arg2_name: float, arg3_name: float],] ...) ERROR: Unmatched ']' at testData/types/invalid.nut.txt:34:71 function_name(arg1_name: number, [arg2_name: float, arg3_name: float], [...]) ERROR: Optional block must be the last argument at testData/types/invalid.nut.txt:35:72 function_name(arg1_name: number, [arg2_name: float, arg3_name: float], ... ERROR: Expected ')' after ellipsis argument at testData/types/invalid.nut.txt:36:75 function_name(arg1_name: number, [arg2_name: float, arg3_name: float], ...( ERROR: Expected ')' after ellipsis argument at testData/types/invalid.nut.txt:37:75 pure_ function_name() ERROR: Expected '(' after function name at testData/types/invalid.nut.txt:38:7 nodiscard_ function_name() ERROR: Expected '(' after function name at testData/types/invalid.nut.txt:39:12 pure nodiscard_ function_name() ERROR: Expected '(' after function name at testData/types/invalid.nut.txt:40:17 ================================================ FILE: testData/types/type_suggestions.nut.txt ================================================ fn(x: Float) fn(x: float|None) closure.fn() string.fn():integer ================================================ FILE: testData/types/type_suggestions.out ================================================ fn(x: Float) ERROR: Invalid type name 'Float', did you mean 'float'? at testData/types/type_suggestions.nut.txt:1:12 fn(x: float|None) ERROR: Invalid type name 'None', did you mean 'null'? at testData/types/type_suggestions.nut.txt:2:17 closure.fn() ERROR: Invalid type name 'closure', did you mean 'function'? at testData/types/type_suggestions.nut.txt:3:7 string.fn():integer ERROR: Invalid type name 'integer', did you mean 'int'? at testData/types/type_suggestions.nut.txt:4:20 ================================================ FILE: testData/types/valid.nut.txt ================================================ function_name([arg2_name: float, arg3_name: float]) function_name([arg2_name: float, arg3_name: float], ...) function_name(arg1_name: string, [arg2_name: int, arg3_name: table], ...: string): any FunctionName(arg1_Name: bool|null) function_name(arg1_name: int, arg2_name: string, arg3_name): float|null function_name(arg1_name: table, arg2_name: array, arg3_name: class): function|null function_name(arg1_name, [arg2_name, arg3_name]) function_name([arg1_name: instance, arg2_name: class, arg3_name: int]): table instance.function_name() (userpointer|instance).function_name() (table|class).function_name() function_name(...) function_name(arg1_name: int, ...) function_name(arg1_name: number, [arg2_name: float, arg3_name: float], ...) string.function_name(int: float, [string: number, string: generator]) function_name(arg1_name: bool, [arg2_name: string, arg3_name: float]) function_name(arg1_name: int, [arg2_name: int, arg3_name: int]) function_name() function_name(): int function_name(arg1_name) FunctionName(arg1_Name: int|null) FunctionName(arg1_Name: table|array) function_name([arg2_name: float, arg3_name: float]) function_name([arg2_name: float, arg3_name: float], ...) function_name(arg1_name: string, [arg2_name: int, arg3_name: table, ...: string]) : any FunctionName(arg1_Name: ( null | bool ) ) function_name(arg1_name: int, arg2_name: string, arg3_name: any): float | null function_name(arg1_name: table, arg2_name: array, arg3_name: class): (function | null) function_name(arg1_name: any, [arg2_name: any, arg3_name: any]) function_name([arg1_name: instance, arg2_name: class, arg3_name: int]): table instance.function_name() (instance|userpointer).function_name() ( table | class ) . function_name ( ) function_name(...: any) function_name(...) function_name(arg1_name: int, ...) function_name(arg1_name: number, [arg2_name: float, arg3_name: float], ...) string.function_name(int: float, [string: number, string: generator]) function_name(arg1_name:bool,[arg2_name:string,arg3_name:float]) function_name ( arg1_name :int ,[ arg2_name: int, arg3_name : int ] ) function_name() function_name(): int function_name(arg1_name: any) FunctionName(arg1_Name: int|null) FunctionName(arg1_Name: (table|array)) pure function_name(x) nodiscard function_name(x) pure nodiscard function_name(x) nodiscard pure function_name(x) ================================================ FILE: testData/types/valid.out ================================================ function_name([arg2_name: float, arg3_name: float]) function_name([arg2_name: float, arg3_name: float]) functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 2 pure: false nodiscard: false function_name([arg2_name: float, arg3_name: float], ...) function_name([arg2_name: float, arg3_name: float], ...) functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0xffffffff requiredArgs: 0 argCount: 2 pure: false nodiscard: false function_name(arg1_name: string, [arg2_name: int, arg3_name: table], ...: string): any function_name(arg1_name: string, [arg2_name: int, arg3_name: table], ...: string): any functionName: function_name returnTypeMask: 0xffffffff objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x10 requiredArgs: 1 argCount: 3 pure: false nodiscard: false FunctionName(arg1_Name: bool|null) FunctionName(arg1_Name: bool|null) functionName: FunctionName returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 1 argCount: 1 pure: false nodiscard: false function_name(arg1_name: int, arg2_name: string, arg3_name): float|null function_name(arg1_name: int, arg2_name: string, arg3_name): float|null functionName: function_name returnTypeMask: 0x5 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 3 argCount: 3 pure: false nodiscard: false function_name(arg1_name: table, arg2_name: array, arg3_name: class): function|null function_name(arg1_name: table, arg2_name: array, arg3_name: class): function|null functionName: function_name returnTypeMask: 0x301 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 3 argCount: 3 pure: false nodiscard: false function_name(arg1_name, [arg2_name, arg3_name]) function_name(arg1_name, [arg2_name, arg3_name]) functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 1 argCount: 3 pure: false nodiscard: false function_name([arg1_name: instance, arg2_name: class, arg3_name: int]): table function_name([arg1_name: instance, arg2_name: class, arg3_name: int]): table functionName: function_name returnTypeMask: 0x20 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 3 pure: false nodiscard: false instance.function_name() instance.function_name() functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0x8000 ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 0 pure: false nodiscard: false (userpointer|instance).function_name() (userpointer|instance).function_name() functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0x8800 ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 0 pure: false nodiscard: false (table|class).function_name() (table|class).function_name() functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0x4020 ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 0 pure: false nodiscard: false function_name(...) function_name(...) functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0xffffffff requiredArgs: 0 argCount: 0 pure: false nodiscard: false function_name(arg1_name: int, ...) function_name(arg1_name: int, ...) functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0xffffffff requiredArgs: 1 argCount: 1 pure: false nodiscard: false function_name(arg1_name: number, [arg2_name: float, arg3_name: float], ...) function_name(arg1_name: number, [arg2_name: float, arg3_name: float], ...) functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0xffffffff requiredArgs: 1 argCount: 3 pure: false nodiscard: false string.function_name(int: float, [string: number, string: generator]) string.function_name(int: float, [string: number, string: generator]) functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0x10 ellipsisArgTypeMask: 0x0 requiredArgs: 1 argCount: 3 pure: false nodiscard: false function_name(arg1_name: bool, [arg2_name: string, arg3_name: float]) function_name(arg1_name: bool, [arg2_name: string, arg3_name: float]) functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 1 argCount: 3 pure: false nodiscard: false function_name(arg1_name: int, [arg2_name: int, arg3_name: int]) function_name(arg1_name: int, [arg2_name: int, arg3_name: int]) functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 1 argCount: 3 pure: false nodiscard: false function_name() function_name() functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 0 pure: false nodiscard: false function_name(): int function_name(): int functionName: function_name returnTypeMask: 0x2 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 0 pure: false nodiscard: false function_name(arg1_name) function_name(arg1_name) functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 1 argCount: 1 pure: false nodiscard: false FunctionName(arg1_Name: int|null) FunctionName(arg1_Name: int|null) functionName: FunctionName returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 1 argCount: 1 pure: false nodiscard: false FunctionName(arg1_Name: table|array) FunctionName(arg1_Name: table|array) functionName: FunctionName returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 1 argCount: 1 pure: false nodiscard: false function_name([arg2_name: float, arg3_name: float]) function_name([arg2_name: float, arg3_name: float]) functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 2 pure: false nodiscard: false function_name([arg2_name: float, arg3_name: float], ...) function_name([arg2_name: float, arg3_name: float], ...) functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0xffffffff requiredArgs: 0 argCount: 2 pure: false nodiscard: false function_name(arg1_name: string, [arg2_name: int, arg3_name: table, ...: string]) : any function_name(arg1_name: string, [arg2_name: int, arg3_name: table], ...: string): any functionName: function_name returnTypeMask: 0xffffffff objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x10 requiredArgs: 1 argCount: 3 pure: false nodiscard: false FunctionName(arg1_Name: ( null | bool ) ) FunctionName(arg1_Name: bool|null) functionName: FunctionName returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 1 argCount: 1 pure: false nodiscard: false function_name(arg1_name: int, arg2_name: string, arg3_name: any): float | null function_name(arg1_name: int, arg2_name: string, arg3_name): float|null functionName: function_name returnTypeMask: 0x5 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 3 argCount: 3 pure: false nodiscard: false function_name(arg1_name: table, arg2_name: array, arg3_name: class): (function | null) function_name(arg1_name: table, arg2_name: array, arg3_name: class): function|null functionName: function_name returnTypeMask: 0x301 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 3 argCount: 3 pure: false nodiscard: false function_name(arg1_name: any, [arg2_name: any, arg3_name: any]) function_name(arg1_name, [arg2_name, arg3_name]) functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 1 argCount: 3 pure: false nodiscard: false function_name([arg1_name: instance, arg2_name: class, arg3_name: int]): table function_name([arg1_name: instance, arg2_name: class, arg3_name: int]): table functionName: function_name returnTypeMask: 0x20 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 3 pure: false nodiscard: false instance.function_name() instance.function_name() functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0x8000 ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 0 pure: false nodiscard: false (instance|userpointer).function_name() (userpointer|instance).function_name() functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0x8800 ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 0 pure: false nodiscard: false ( table | class ) . function_name ( ) (table|class).function_name() functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0x4020 ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 0 pure: false nodiscard: false function_name(...: any) function_name(...) functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0xffffffff requiredArgs: 0 argCount: 0 pure: false nodiscard: false function_name(...) function_name(...) functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0xffffffff requiredArgs: 0 argCount: 0 pure: false nodiscard: false function_name(arg1_name: int, ...) function_name(arg1_name: int, ...) functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0xffffffff requiredArgs: 1 argCount: 1 pure: false nodiscard: false function_name(arg1_name: number, [arg2_name: float, arg3_name: float], ...) function_name(arg1_name: number, [arg2_name: float, arg3_name: float], ...) functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0xffffffff requiredArgs: 1 argCount: 3 pure: false nodiscard: false string.function_name(int: float, [string: number, string: generator]) string.function_name(int: float, [string: number, string: generator]) functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0x10 ellipsisArgTypeMask: 0x0 requiredArgs: 1 argCount: 3 pure: false nodiscard: false function_name(arg1_name:bool,[arg2_name:string,arg3_name:float]) function_name(arg1_name: bool, [arg2_name: string, arg3_name: float]) functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 1 argCount: 3 pure: false nodiscard: false function_name ( arg1_name :int ,[ arg2_name: int, arg3_name : int ] ) function_name(arg1_name: int, [arg2_name: int, arg3_name: int]) functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 1 argCount: 3 pure: false nodiscard: false function_name() function_name() functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 0 pure: false nodiscard: false function_name(): int function_name(): int functionName: function_name returnTypeMask: 0x2 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 0 argCount: 0 pure: false nodiscard: false function_name(arg1_name: any) function_name(arg1_name) functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 1 argCount: 1 pure: false nodiscard: false FunctionName(arg1_Name: int|null) FunctionName(arg1_Name: int|null) functionName: FunctionName returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 1 argCount: 1 pure: false nodiscard: false FunctionName(arg1_Name: (table|array)) FunctionName(arg1_Name: table|array) functionName: FunctionName returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 1 argCount: 1 pure: false nodiscard: false pure function_name(x) pure function_name(x) functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 1 argCount: 1 pure: true nodiscard: false nodiscard function_name(x) nodiscard function_name(x) functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 1 argCount: 1 pure: false nodiscard: true pure nodiscard function_name(x) pure nodiscard function_name(x) functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 1 argCount: 1 pure: true nodiscard: true nodiscard pure function_name(x) pure nodiscard function_name(x) functionName: function_name returnTypeMask: 0x1 objectTypeMask: 0xffffffff ellipsisArgTypeMask: 0x0 requiredArgs: 1 argCount: 1 pure: true nodiscard: true ================================================ FILE: testRunner.py ================================================ #!/usr/bin/env python3 import tempfile from os import path import sys import os.path from pathlib import Path import argparse import subprocess from subprocess import Popen, PIPE import platform import threading from concurrent.futures import ThreadPoolExecutor, as_completed CRED = '\33[31m' CGREEN = '\33[32m' RESET = "\033[0;0m" CBOLD = '\33[1m' numOfTests=0 numOfFailedTests=0 verbose=False ciRun=False THREADS = 12 printLock = threading.Lock() def xprint(str, color = ''): global ciRun if (not ciRun): print(color, end='') print(str, end='') if (not ciRun): print(RESET) else: print('') # endline def computePath(path, *paths): return os.path.normpath(os.path.join(path, *paths)) def compareFilesLineByLine(marker, testFile, actualFile, expectedFile): global numOfFailedTests with open(expectedFile) as expected, open(actualFile) as actual: expectedLines = expected.readlines() actualLines = actual.readlines() differs = len(expectedLines) != len(actualLines) if differs == False: i = 0 while i < len(expectedLines): e = expectedLines[i].rstrip() a = actualLines[i].rstrip() i += 1 if e != a: differs = True break if differs == True: xprint("") xprint(f"\nFAIL: {testFile}", CBOLD + CRED) xprint("--Actual------------------", CBOLD + CRED) xprint("".join(actualLines)) xprint("--Expected----------------", CBOLD + CRED) xprint("".join(expectedLines)) xprint("--------------------------", CBOLD + CRED) xprint("") numOfFailedTests += 1 return False return True def updateExpectedFromActualIfNeed(marker, actualFile, expectedFile): if (not path.exists(expectedFile)): xprint(f" info: no {marker} expected file, create it") with open(actualFile) as f: result = f.read() with open(expectedFile, 'w') as f: f.write(result) def runTestGeneric(compiler, workingDir, dirname, name, kind, suffix, extraargs, stdoutFile): global numOfFailedTests global verbose testFilePath = computePath(dirname, name + '.nut') if (not path.exists(testFilePath)): testFilePath = computePath(dirname, name + '.nut.txt') testFilePath = testFilePath.replace('\\', '/') expectedResultFilePath = computePath(dirname, name + suffix) outputDir = computePath(workingDir, dirname) os.makedirs(outputDir, exist_ok=True) actualResultFilePath = computePath(workingDir, expectedResultFilePath) if path.exists(actualResultFilePath): os.remove(actualResultFilePath) compilationCommand = [compiler, "-optCH"] compilationCommand += extraargs if stdoutFile: outredirect = open(actualResultFilePath, 'w+') else: outredirect = subprocess.PIPE compilationCommand += [actualResultFilePath] compilationCommand += [testFilePath] if verbose: with printLock: xprint(compilationCommand) proc = Popen(compilationCommand, stdout=outredirect, stderr=outredirect) outs = None errs = None try: outs, errs = proc.communicate(timeout=10) except subprocess.TimeoutExpired: proc.kill() outs, errs = proc.communicate() if stdoutFile and outredirect: outredirect.close() with printLock: xprint("\nTIMEOUT: sq froze on test: {0}\n".format(testFilePath), CBOLD + CRED) numOfFailedTests = numOfFailedTests + 1 return if stdoutFile and outredirect: outredirect.close() with printLock: if proc.returncode != 0: xprint("\nCRASH: {0}".format(testFilePath), CBOLD + CRED) numOfFailedTests = numOfFailedTests + 1 if outs != None: xprint("--stdout------------------", CBOLD + CRED) xprint(outs.decode('utf-8')) if errs != None: xprint("--stderr------------------", CBOLD + CRED) xprint(errs.decode('utf-8')) if errs != None or outs != None: xprint("--------------------------", CBOLD + CRED) xprint("") else: testOk = True if (path.exists(expectedResultFilePath)): testOk = compareFilesLineByLine(kind, testFilePath, actualResultFilePath, expectedResultFilePath) if (testOk): xprint(f"PASSED: {testFilePath}", CBOLD + CGREEN) updateExpectedFromActualIfNeed(kind, actualResultFilePath, expectedResultFilePath) def runDiagTest(compiler, workingDir, dirname, name): runTestGeneric(compiler, workingDir, dirname, name, "Diagnostics", '.diag.txt', ["-diag-file"], False) def runSATest(compiler, workingDir, dirname, name): runTestGeneric(compiler, workingDir, dirname, name, "Static Analyzer", '.diag.txt', ["-sa", "-diag-file"], False) def runExecuteTest(compiler, workingDir, dirname, name): runTestGeneric(compiler, workingDir, dirname, name, "Exec", '.out', ["--check-stack"], True) def runParseTypesTest(compiler, workingDir, dirname, name): runTestGeneric(compiler, workingDir, dirname, name, "Parse Types", '.out', ["--parse-types"], True) def runASTTest(compiler, workingDir, dirname, name): runTestGeneric(compiler, workingDir, dirname, name, "AST", '.opt.txt', ["-ast-dump"], False) def runTestForData(filePath, compiler, workingDir, testMode): global numOfFailedTests global numOfTests with printLock: numOfTests = numOfTests + 1 basename = os.path.basename(filePath) dirname = os.path.dirname(filePath) index_of_dot = basename.index('.') name = basename[:index_of_dot] suffix = basename[index_of_dot:] if suffix == ".nut" or suffix == ".nut.txt": if testMode == 'ast': runASTTest(compiler, workingDir, dirname, name) elif testMode == 'diag': runDiagTest(compiler, workingDir, dirname, name) elif testMode == 'exec': runExecuteTest(compiler, workingDir, dirname, name) elif testMode == 'types': runParseTypesTest(compiler, workingDir, dirname, name) elif testMode == 'sa': runSATest(compiler, workingDir, dirname, name) else: with printLock: xprint(f"Unknown test mode {testMode}") def collectTests(subdir, mode): p = Path(computePath('testData', subdir)) return [(str(f), mode) for f in sorted(p.rglob('*')) if f.is_file()] def checkCompiler(compiler): try: if not path.exists(compiler): xprint('FAIL: compiler executable \'{0}\' not found'.format(compiler)) exit(1) compProc = Popen([compiler, '-v']) compProc.communicate() if compProc.returncode != 0: xprint('FAIL: {0} is not an sq compiler'.format(compiler)) exit(1) except: xprint('FAIL: {0} is not an sq compiler'.format(compiler)) exit(1) def main(): global numOfFailedTests global verbose, ciRun default_sq = computePath('build', 'bin', 'Debug', 'sq.exe' if platform.system() == 'Windows' else 'sq') parser = argparse.ArgumentParser(description='quirrel test runner') parser.add_argument('-sq', '--squirrel', action="store", type=str, default = default_sq, help = "Path to Quirrel compiler") parser.add_argument('-wd', '--working-dir', action="store", type=str, default = tempfile.TemporaryDirectory().name, help = "Directory to store tests data like ast dumps, diganotics dumps and so on") parser.add_argument('-v', '--verbose', default = False, action="store_true", help = "Extra output like CLI commands") parser.add_argument('-ci', '--continious-integration', default = False, action="store_true", help = "Signals that script is being run under CI") args = parser.parse_args() compiler = args.squirrel workingDir = args.working_dir verbose = args.verbose ciRun = args.continious_integration if platform.system() == 'Windows' and not compiler.endswith('.exe'): compiler += '.exe' checkCompiler(compiler) allTests = [] allTests += collectTests('exec', 'exec') allTests += collectTests('diagnostics', 'diag') allTests += collectTests('ast', 'ast') allTests += collectTests('static_analyzer', 'sa') allTests += collectTests('types', 'types') with ThreadPoolExecutor(max_workers=THREADS) as pool: futures = [pool.submit(runTestForData, f, compiler, workingDir, mode) for f, mode in allTests] for fut in as_completed(futures): fut.result() if numOfFailedTests: xprint(f"Failed tests: {numOfFailedTests}", CBOLD + CRED) else: xprint(f"All tests passed", CBOLD + CGREEN) exit (1 if numOfFailedTests > 0 else 0) if __name__ == "__main__": main()