├── .cargo └── config.toml ├── .github ├── FUNDING.yml └── workflows │ ├── cargo-test.yml │ └── release.yml ├── .gitignore ├── .vscode └── launch.json ├── Cargo.lock ├── Cargo.toml ├── LICENSE ├── README.md ├── aiscript-arena ├── Cargo.toml └── src │ ├── allocator_api.rs │ ├── arena.rs │ ├── barrier.rs │ ├── collect.rs │ ├── collect_impl.rs │ ├── context.rs │ ├── dynamic_roots.rs │ ├── gc.rs │ ├── gc_weak.rs │ ├── hashbrown.rs │ ├── lib.rs │ ├── lock.rs │ ├── metrics.rs │ ├── no_drop.rs │ ├── static_collect.rs │ ├── types.rs │ └── unsize.rs ├── aiscript-common ├── Cargo.toml └── src │ └── lib.rs ├── aiscript-derived ├── Cargo.toml └── src │ └── lib.rs ├── aiscript-directive ├── Cargo.toml └── src │ ├── lib.rs │ ├── route │ ├── docs.rs │ └── mod.rs │ └── validator │ ├── array.rs │ ├── date.rs │ ├── format.rs │ ├── mod.rs │ └── regex.rs ├── aiscript-lexer ├── Cargo.toml └── src │ ├── character_tests.rs │ ├── error_reporter.rs │ ├── fstring_tests.rs │ ├── lib.rs │ ├── peakable.rs │ └── tests.rs ├── aiscript-runtime ├── Cargo.toml └── src │ ├── ast │ └── mod.rs │ ├── config │ ├── auth.rs │ ├── db.rs │ ├── mod.rs │ ├── sso.rs │ └── tests.rs │ ├── endpoint.rs │ ├── error.rs │ ├── lib.rs │ ├── openapi │ ├── mod.rs │ ├── redoc.html │ └── swagger.html │ ├── parser.rs │ └── utils.rs ├── aiscript-vm ├── Cargo.toml └── src │ ├── ai │ ├── agent.rs │ ├── mod.rs │ └── prompt.rs │ ├── ast │ ├── mod.rs │ └── pretty.rs │ ├── builtins │ ├── array.rs │ ├── convert.rs │ ├── error.rs │ ├── format.rs │ ├── function.rs │ ├── mod.rs │ ├── print.rs │ ├── response.rs │ ├── sso.rs │ └── string.rs │ ├── chunk.rs │ ├── compiler │ ├── codegen.rs │ ├── mod.rs │ └── optimizer │ │ ├── dead_code.rs │ │ ├── mod.rs │ │ └── pop_combine.rs │ ├── lib.rs │ ├── module.rs │ ├── object.rs │ ├── parser │ ├── mod.rs │ └── stmt_test.rs │ ├── stdlib │ ├── auth │ │ ├── jwt.rs │ │ └── mod.rs │ ├── db │ │ ├── mod.rs │ │ ├── pg.rs │ │ ├── redis.rs │ │ └── sqlite.rs │ ├── env.rs │ ├── http.rs │ ├── io.rs │ ├── math.rs │ ├── mod.rs │ ├── random.rs │ ├── serde.rs │ └── time.rs │ ├── string │ ├── interned.rs │ ├── mod.rs │ └── utils.rs │ ├── ty │ ├── enum.rs │ ├── error.rs │ ├── mod.rs │ └── resolver.rs │ ├── value.rs │ └── vm │ ├── extra.rs │ ├── fuel.rs │ ├── mod.rs │ └── state.rs ├── aiscript ├── Cargo.toml └── src │ ├── main.rs │ ├── project.rs │ └── repr.rs ├── assets ├── logo.svg └── toucan.png ├── dist-workspace.toml ├── examples ├── agent.ai ├── agent.json ├── array.ai ├── claude.ai ├── enum.ai ├── env.ai ├── error_handle.ai ├── http.ai ├── lambda.ai ├── main.ai ├── ollama.ai ├── project.toml ├── redis.ai ├── routes │ ├── ai.ai │ ├── auth.ai │ ├── nested │ │ └── gist.ai │ ├── path.ai │ ├── poc.ai │ ├── regex.ai │ └── repo.ai ├── serde.ai ├── sql.ai ├── test.ai ├── tuple.ai ├── user_math.ai └── validation.ai ├── publish.sh ├── tests ├── Cargo.toml ├── integration.rs └── integration │ ├── ai │ ├── ai_function.ai │ ├── ai_function_keyword.ai │ ├── ai_function_missing.ai │ ├── prompt.ai │ └── unsupported_model.ai │ ├── array │ ├── equality.ai │ ├── invalid_syntax.ai │ ├── normal.ai │ └── single_element.ai │ ├── assignment │ ├── associativity.ai │ ├── global.ai │ ├── grouping.ai │ ├── infix_operator.ai │ ├── local.ai │ ├── prefix_operator.ai │ ├── syntax.ai │ ├── to_self.ai │ └── undefined.ai │ ├── block │ ├── empty.ai │ └── scope.ai │ ├── bool │ ├── equality.ai │ └── not.ai │ ├── break_continue │ ├── break_syntax.ai │ ├── continue_syntax.ai │ └── outside_loop.ai │ ├── builtin_functions │ ├── ascii_chr_ord.ai │ ├── bin_hex_oct.ai │ ├── bool.ai │ ├── callable.ai │ ├── filter.ai │ ├── float.ai │ ├── format.ai │ ├── int.ai │ ├── invalid_arg_count.ai │ ├── map.ai │ ├── normal.ai │ ├── print.ai │ ├── str.ai │ └── zip.ai │ ├── builtin_methods │ ├── array.ai │ └── string.ai │ ├── call │ ├── bool.ai │ ├── nil.ai │ ├── num.ai │ ├── object.ai │ └── string.ai │ ├── class │ ├── empty.ai │ ├── inherit_self.ai │ ├── inherited_method.ai │ ├── local_inherit_other.ai │ ├── local_inherit_self.ai │ ├── local_reference_self.ai │ ├── methods.ai │ └── reference_self.ai │ ├── class_fields │ ├── class_as_type_hint.ai │ ├── empty_constructor.ai │ ├── explicit_constructor.ai │ ├── implicit_constructor.ai │ └── syntax_error.ai │ ├── class_literal │ ├── fields_checking.ai │ └── initialize.ai │ ├── closure │ ├── assign_to_closure.ai │ ├── assign_to_shadowed_later.ai │ ├── close_over_function_parameter.ai │ ├── close_over_later_variable.ai │ ├── close_over_method_parameter.ai │ ├── closed_closure_in_function.ai │ ├── nested_closure.ai │ ├── open_closure_in_function.ai │ ├── reference_closure_multiple_times.ai │ ├── reuse_closure_slot.ai │ ├── shadow_closure_with_local.ai │ ├── unused_closure.ai │ └── unused_later_closure.ai │ ├── comments │ ├── line_at_eof.ai │ ├── only_line_comment.ai │ ├── only_line_comment_and_line.ai │ └── unicode.ai │ ├── compound_assign │ ├── add.ai │ ├── add_nonnum_num.ai │ ├── add_num_nonnum.ai │ ├── all.ai │ ├── divide.ai │ ├── divide_nonnum_num.ai │ ├── divide_num_nonnum.ai │ ├── minus.ai │ ├── minus_nonnum_num.ai │ ├── minus_num_nonnum.ai │ ├── modulo.ai │ ├── modulo_nonnum_num.ai │ ├── modulo_num_nonnum.ai │ ├── multiply.ai │ ├── multiply_nonnum_num.ai │ ├── multiply_num_nonnum.ai │ └── str.ai │ ├── const │ ├── globals.ai │ ├── initializer.ai │ ├── locals.ai │ ├── shadow.ai │ └── upvalues.ai │ ├── constructor │ ├── default_args │ ├── duplicate_keyword_args.ai │ ├── enum_variant_default_value.ai │ ├── invalid_default_args_declare_order.ai │ ├── invalid_enum_arg_type.ai │ ├── invalid_enum_variant_arg_type.ai │ ├── invalid_keword_arg.ai │ ├── missing_required_arg.ai │ ├── normal.ai │ └── positional_arg_position_invalid.ai │ ├── directive_validators │ └── syntax.ai │ ├── empty_file.ai │ ├── enum │ ├── enum_double_colon.ai │ ├── enum_invalid_method1.ai │ ├── enum_invalid_method2.ai │ ├── enum_invalid_method3.ai │ ├── enum_method.ai │ ├── enum_variant_assign.ai │ ├── equality.ai │ ├── expect_name.ai │ ├── integer_value.ai │ ├── invalid_enum.ai │ ├── invalid_enum_inherit.ai │ ├── invalid_enum_value_type.ai │ ├── invalid_enum_variant.ai │ ├── invalid_variant.ai │ ├── invalid_variant_access_value.ai │ ├── missing_punct.ai │ ├── syntax.ai │ └── variant_type_check.ai │ ├── enum_evaluate │ ├── array.ai │ ├── not_enum_variant1.ai │ ├── not_enum_variant2.ai │ ├── not_enum_variant3.ai │ └── syntax.ai │ ├── env │ ├── normal.ai │ ├── syntax.ai │ └── undefined_variable.ai │ ├── error_handling │ ├── cannot_define_error_handler_for_lambda.ai │ ├── error_signatures.ai │ ├── last_error_block_expr_is_function_call_value.ai │ ├── pipe_chain.ai │ ├── propagation.ai │ ├── raise_catch.ai │ ├── raise_error_in_lambda.ai │ └── raise_outside_declared_function.ai │ ├── field │ ├── call_function_field.ai │ ├── call_nonfunction_field.ai │ ├── get_and_set_method.ai │ ├── get_on_bool.ai │ ├── get_on_class.ai │ ├── get_on_function.ai │ ├── get_on_nil.ai │ ├── get_on_num.ai │ ├── get_on_string.ai │ ├── many.ai │ ├── method.ai │ ├── method_binds_self.ai │ ├── on_instance.ai │ ├── set_evaluation_order.ai │ ├── set_on_bool.ai │ ├── set_on_class.ai │ ├── set_on_function.ai │ ├── set_on_nil.ai │ ├── set_on_num.ai │ ├── set_on_string.ai │ └── undefined.ai │ ├── for │ ├── class_in_body.ai │ ├── closure_in_body.ai │ ├── fun_in_body.ai │ ├── object_literal.ai │ ├── return_closure.ai │ ├── return_inside.ai │ ├── scope.ai │ ├── statement_condition.ai │ ├── statement_initializer.ai │ ├── syntax.ai │ └── var_in_body.ai │ ├── fstring │ ├── basic.ai │ ├── edge_cases.ai │ ├── expression.ai │ └── types.ai │ ├── function │ ├── body_must_be_block.ai │ ├── duplicate_fn.ai │ ├── empty_body.ai │ ├── expect_type_after_arrow.ai │ ├── extra_arguments.ai │ ├── invalid_parameter_type.ai │ ├── last_expr_implicit_return.ai │ ├── local_mutual_recursion.ai │ ├── local_recursion.ai │ ├── missing_arguments.ai │ ├── missing_comma_in_parameters.ai │ ├── mutual_recursion.ai │ ├── nested_call_with_arguments.ai │ ├── parameters.ai │ ├── paramter_return_types.ai │ ├── print.ai │ ├── recursion.ai │ ├── too_many_arguments.ai │ ├── too_many_parameters.ai │ ├── undefined_parameter_type.ai │ └── undefined_return_type.ai │ ├── helloworld.ai │ ├── if │ ├── dangling_else.ai │ ├── else.ai │ ├── if.ai │ ├── missing_close_paren.ai │ ├── missing_open_paren.ai │ ├── object_literal_in_condition.ai │ └── truth.ai │ ├── in │ ├── invalid_array_key.ai │ ├── invalid_object_key.ai │ ├── normal.ai │ └── string_in_string.ai │ ├── inheritance │ ├── constructor.ai │ ├── inherit_from_function.ai │ ├── inherit_from_nil.ai │ ├── inherit_from_number.ai │ ├── inherit_methods.ai │ ├── inherit_missing_close_paren.ai │ ├── parenthesized_superclass.ai │ └── set_fields_from_base_class.ai │ ├── inline_if │ ├── invalid_syntax.ai │ └── normal.ai │ ├── lambda │ ├── last_expr_implicit_return.ai │ ├── normal.ai │ ├── syntax.ai │ ├── too_many_arguments.ai │ └── upvalue_captures.ai │ ├── limit │ ├── loop_too_large.ai │ ├── no_reuse_constants.ai │ ├── stack_overflow.ai │ ├── too_many_constants.ai │ ├── too_many_locals.ai │ └── too_many_upvalues.ai │ ├── logical_operator │ ├── and.ai │ ├── and_truth.ai │ ├── or.ai │ └── or_truth.ai │ ├── match │ ├── bool.ai │ ├── empty.ai │ ├── enum.ai │ ├── error.ai │ ├── global_scope.ai │ ├── mutiple_patterns.ai │ ├── number.ai │ ├── range.ai │ └── string.ai │ ├── method │ ├── arity.ai │ ├── empty_block.ai │ ├── extra_arguments.ai │ ├── missing_arguments.ai │ ├── not_found.ai │ ├── print_bound_method.ai │ ├── refer_to_name.ai │ ├── too_many_arguments.ai │ └── too_many_parameters.ai │ ├── module │ └── invalid_syntax.ai │ ├── nil │ └── literal.ai │ ├── number │ ├── decimal_point_at_eof.ai │ ├── leading_dot.ai │ ├── literals.ai │ ├── nan_equality.ai │ └── trailing_dot.ai │ ├── object │ ├── computed_key.ai │ ├── equality.ai │ ├── invalid_syntax.ai │ ├── normal.ai │ ├── shorthand.ai │ └── string_keys.ai │ ├── operator │ ├── add.ai │ ├── add_bool_nil.ai │ ├── add_bool_num.ai │ ├── add_bool_string.ai │ ├── add_nil_nil.ai │ ├── add_num_nil.ai │ ├── add_string_nil.ai │ ├── comparison.ai │ ├── divide.ai │ ├── divide_nonnum_num.ai │ ├── divide_num_nonnum.ai │ ├── equals.ai │ ├── equals_class.ai │ ├── equals_method.ai │ ├── greater_nonnum_num.ai │ ├── greater_num_nonnum.ai │ ├── greater_or_equal_nonnum_num.ai │ ├── greater_or_equal_num_nonnum.ai │ ├── less_nonnum_num.ai │ ├── less_num_nonnum.ai │ ├── less_or_equal_nonnum_num.ai │ ├── less_or_equal_num_nonnum.ai │ ├── modulo.ai │ ├── modulo_nonnum_num.ai │ ├── modulo_num_nonnum.ai │ ├── multiply.ai │ ├── multiply_nonnum_num.ai │ ├── multiply_num_nonnum.ai │ ├── negate.ai │ ├── negate_nonnum.ai │ ├── not.ai │ ├── not_class.ai │ ├── not_equals.ai │ ├── power.ai │ ├── power_nonnum_num.ai │ ├── power_num_nonnum.ai │ ├── subtract.ai │ ├── subtract_nonnum_num.ai │ └── subtract_num_nonnum.ai │ ├── pipe │ ├── expr.ai │ ├── invalid_arg_count.ai │ ├── invalid_syntax.ai │ ├── normal.ai │ ├── precedence.ai │ └── single_arg_function.ai │ ├── precedence.ai │ ├── regression │ ├── 394.ai │ └── 40.ai │ ├── return │ ├── after_else.ai │ ├── after_if.ai │ ├── after_while.ai │ ├── in_function.ai │ ├── in_method.ai │ └── return_nil_if_no_value.ai │ ├── self │ ├── closure.ai │ ├── constructor.ai │ ├── invalid_self.ai │ ├── multiple_self.ai │ ├── nested_class.ai │ ├── nested_closure.ai │ ├── self_at_top_level.ai │ ├── self_in_class_method_closure.ai │ ├── self_in_class_method_lambda.ai │ ├── self_in_class_static_method.ai │ ├── self_in_enum_method_closure.ai │ ├── self_in_enum_method_lambda.ai │ ├── self_in_enum_static_method.ai │ ├── self_in_top_level_function.ai │ └── self_outside_of_class.ai │ ├── static_method │ ├── class.ai │ ├── class_self_call_static_method.ai │ ├── enum.ai │ ├── enum_self_call_static_method.ai │ ├── instance_call_static_method1.ai │ ├── instance_call_static_method2.ai │ ├── super_in_static_method.ai │ ├── variant_call_static_method1.ai │ └── variant_call_static_method2.ai │ ├── stdlib │ └── auth │ │ └── jwt.ai │ ├── string │ ├── error_after_multiline.ai │ ├── escape.ai │ ├── literals.ai │ ├── multiline.ai │ ├── raw_string.ai │ └── unterminated.ai │ ├── super │ ├── bound_method.ai │ ├── call_other_method.ai │ ├── call_same_method.ai │ ├── closure.ai │ ├── constructor.ai │ ├── extra_arguments.ai │ ├── indirectly_inherited.ai │ ├── missing_arguments.ai │ ├── no_superclass_bind.ai │ ├── no_superclass_call.ai │ ├── no_superclass_method.ai │ ├── parenthesized.ai │ ├── reassign_superclass.ai │ ├── self_in_superclass_method.ai │ ├── super_as_parameter.ai │ ├── super_at_top_level.ai │ ├── super_in_closure_in_inherited_method.ai │ ├── super_in_enum.ai │ ├── super_in_inherited_method.ai │ ├── super_in_top_level_function.ai │ ├── super_without_dot.ai │ └── super_without_name.ai │ ├── tuple │ ├── index.ai │ ├── nested.ai │ └── syntax.ai │ ├── unexpected_character.ai │ ├── validation │ └── validate_string.ai │ ├── variable │ ├── collide_with_parameter.ai │ ├── duplicate_local.ai │ ├── duplicate_parameter.ai │ ├── early_bound.ai │ ├── in_middle_of_block.ai │ ├── in_nested_block.ai │ ├── local_from_method.ai │ ├── redeclare_global.ai │ ├── redefine_global.ai │ ├── scope_reuse_in_different_blocks.ai │ ├── shadow_and_local.ai │ ├── shadow_global.ai │ ├── shadow_local.ai │ ├── undefined_global.ai │ ├── undefined_local.ai │ ├── uninitialized.ai │ ├── unreached_undefined.ai │ ├── use_false_as_var.ai │ ├── use_global_in_initializer.ai │ ├── use_local_in_initializer.ai │ ├── use_nil_as_var.ai │ └── use_self_as_var.ai │ └── while │ ├── closure_in_body.ai │ ├── missing_close_paren.ai │ ├── missing_open_paren.ai │ ├── object_literal_in_condition.ai │ ├── return_closure.ai │ ├── return_inside.ai │ └── syntax.ai └── xtask ├── Cargo.toml └── src └── main.rs /.cargo/config.toml: -------------------------------------------------------------------------------- 1 | [alias] 2 | xtask = "run --package xtask --" 3 | debug = "run --package xtask -- debug" 4 | optimizer = "run --package xtask -- optimizer" 5 | xtest = "run --package xtask -- test" 6 | -------------------------------------------------------------------------------- /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | github: [Folyd] -------------------------------------------------------------------------------- /.github/workflows/cargo-test.yml: -------------------------------------------------------------------------------- 1 | name: Cargo Test 2 | 3 | on: 4 | push: 5 | branches: [main] 6 | pull_request: 7 | branches: [main] 8 | 9 | env: 10 | CARGO_TERM_COLOR: always 11 | 12 | jobs: 13 | test: 14 | name: Test 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v3 18 | 19 | - name: Setup Rust 20 | uses: actions-rs/toolchain@v1 21 | with: 22 | profile: minimal 23 | toolchain: stable 24 | override: true 25 | components: rustfmt, clippy 26 | 27 | - name: Cargo fmt 28 | uses: actions-rs/cargo@v1 29 | with: 30 | command: fmt 31 | args: --all -- --check 32 | 33 | - name: Cargo clippy 34 | uses: actions-rs/cargo@v1 35 | with: 36 | command: clippy 37 | args: -- -D warnings 38 | 39 | - name: Cargo test 40 | uses: actions-rs/cargo@v1 41 | with: 42 | command: xtest 43 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | 3 | .DS_Store 4 | .env -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "type": "lldb", 9 | "request": "launch", 10 | "name": "Debug script", 11 | "cargo": { 12 | "args": ["build", "--bin=aiscript-cli", "--package=aiscript-cli"], 13 | "filter": { 14 | "name": "aiscript-cli", 15 | "kind": "bin" 16 | } 17 | }, 18 | "args": ["main.ai"], 19 | "cwd": "${workspaceFolder}" 20 | }, 21 | { 22 | "type": "lldb", 23 | "request": "launch", 24 | "name": "Debug serve", 25 | "cargo": { 26 | "args": ["build", "--bin=aiscript-cli", "--package=aiscript-cli"], 27 | "filter": { 28 | "name": "aiscript-cli", 29 | "kind": "bin" 30 | } 31 | }, 32 | "args": ["serve", "route.ai"], 33 | "cwd": "${workspaceFolder}" 34 | } 35 | ] 36 | } 37 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = [ 3 | "aiscript", 4 | "aiscript-lexer", 5 | "aiscript-arena", 6 | "aiscript-common", 7 | "aiscript-derived", 8 | "aiscript-directive", 9 | "aiscript-runtime", 10 | "aiscript-vm", 11 | "tests", 12 | "xtask", 13 | ] 14 | exclude = ["assets", "examples", "routes", "tests", "xtask", "project.toml"] 15 | resolver = "2" 16 | 17 | [workspace.package] 18 | version = "0.2.0" 19 | edition = "2024" 20 | homepage = "https://aiscript.dev" 21 | repository = "https://github.com/aiscriptdev/aiscript" 22 | license = "MIT" 23 | authors = ["Folyd lyshuhow@gmail.com"] 24 | keywords = ["language", "interpreter", "scripting", "compiler", "vm"] 25 | categories = ["compilers", "web-programming", "web-programming::http-server"] 26 | 27 | 28 | [workspace.dependencies] 29 | serde = { version = "1.0", features = ["derive"] } 30 | serde_json = "1.0" 31 | redis = { version = "0.29", features = ["aio", "tokio-comp"] } 32 | reqwest = "0.12" 33 | 34 | # RUSTFLAGS="-Z sanitizer=address" cargo run --release --target x86_64-apple-darwin -- test.ai 35 | [profile.release] 36 | debug = true 37 | 38 | # The profile that 'dist' will build with 39 | [profile.dist] 40 | inherits = "release" 41 | lto = "thin" 42 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 AIScript 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /aiscript-arena/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aiscript-arena" 3 | description = "safe, incrementally garbage collected arenas" 4 | version = "0.1.0" 5 | authors.workspace = true 6 | edition.workspace = true 7 | license.workspace = true 8 | readme.workspace = true 9 | repository.workspace = true 10 | 11 | [features] 12 | default = ["std"] 13 | std = [] 14 | tracing = ["dep:tracing"] 15 | allocator-api2 = ["dep:allocator-api2", "hashbrown?/allocator-api2"] 16 | 17 | [dependencies] 18 | allocator-api2 = { version = "0.2", optional = true, default-features = false, features = [ 19 | "alloc", 20 | ] } 21 | aiscript-derived = { path = "../aiscript-derived", version = "0.1.0" } 22 | hashbrown = { version = "0.14", optional = true, default-features = false } 23 | sptr = "0.3.2" 24 | tracing = { version = "0.1.37", optional = true, default-features = false } 25 | 26 | [dev-dependencies] 27 | rand = "0.8" 28 | trybuild = "1.0" 29 | -------------------------------------------------------------------------------- /aiscript-arena/src/lib.rs: -------------------------------------------------------------------------------- 1 | #![no_std] 2 | #![cfg_attr(miri, feature(strict_provenance))] 3 | 4 | #[cfg(feature = "std")] 5 | extern crate std; 6 | 7 | extern crate alloc; 8 | 9 | pub mod arena; 10 | pub mod barrier; 11 | mod collect; 12 | mod collect_impl; 13 | mod context; 14 | pub mod dynamic_roots; 15 | mod gc; 16 | mod gc_weak; 17 | pub mod lock; 18 | pub mod metrics; 19 | mod no_drop; 20 | mod static_collect; 21 | mod types; 22 | mod unsize; 23 | 24 | #[cfg(feature = "allocator-api2")] 25 | pub mod allocator_api; 26 | 27 | #[cfg(feature = "hashbrown")] 28 | mod hashbrown; 29 | 30 | #[doc(hidden)] 31 | pub use aiscript_derived::*; 32 | 33 | #[doc(hidden)] 34 | pub use self::{arena::__DynRootable, no_drop::__MustNotImplDrop, unsize::__CoercePtrInternal}; 35 | 36 | pub use self::{ 37 | arena::{Arena, Rootable}, 38 | collect::Collect, 39 | context::{Collection, Finalization, Mutation}, 40 | dynamic_roots::{DynamicRoot, DynamicRootSet}, 41 | gc::Gc, 42 | gc_weak::GcWeak, 43 | lock::{GcLock, GcRefLock, Lock, RefLock}, 44 | static_collect::Static, 45 | }; 46 | -------------------------------------------------------------------------------- /aiscript-arena/src/no_drop.rs: -------------------------------------------------------------------------------- 1 | // Trait that is automatically implemented for all types that implement `Drop`. 2 | // 3 | // Used to cause a conflicting trait impl if a type implements `Drop` to forbid implementing `Drop`. 4 | #[doc(hidden)] 5 | pub trait __MustNotImplDrop {} 6 | 7 | #[allow(drop_bounds)] 8 | impl __MustNotImplDrop for T {} 9 | -------------------------------------------------------------------------------- /aiscript-common/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aiscript-common" 3 | version.workspace = true 4 | edition.workspace = true 5 | homepage.workspace = true 6 | repository.workspace = true 7 | license.workspace = true 8 | authors.workspace = true 9 | keywords.workspace = true 10 | categories.workspace = true 11 | 12 | [dependencies] 13 | serde.workspace = true 14 | -------------------------------------------------------------------------------- /aiscript-derived/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aiscript-derived" 3 | description = "AIScript derived crate" 4 | version = "0.1.0" 5 | authors.workspace = true 6 | edition.workspace = true 7 | license.workspace = true 8 | readme.workspace = true 9 | repository.workspace = true 10 | 11 | [lib] 12 | proc-macro = true 13 | 14 | [dependencies] 15 | proc-macro2 = "1.0.56" 16 | quote = "1.0.26" 17 | syn = { version = "2.0.17", features = ["default", "visit-mut"] } 18 | synstructure = "0.13" 19 | -------------------------------------------------------------------------------- /aiscript-directive/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aiscript-directive" 3 | description = "Directives of AIScript programming language interpreter" 4 | version.workspace = true 5 | homepage.workspace = true 6 | edition.workspace = true 7 | repository.workspace = true 8 | license.workspace = true 9 | authors.workspace = true 10 | keywords.workspace = true 11 | categories.workspace = true 12 | readme.workspace = true 13 | 14 | [dependencies] 15 | aiscript-lexer = { path = "../aiscript-lexer", version = "0.2.0" } 16 | serde_json = "1.0" 17 | chrono = "0.4" 18 | regex = "1.11" 19 | -------------------------------------------------------------------------------- /aiscript-lexer/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aiscript-lexer" 3 | description = "The lexer of AIScript programming language interpreter" 4 | homepage.workspace = true 5 | version.workspace = true 6 | edition.workspace = true 7 | repository.workspace = true 8 | license.workspace = true 9 | authors.workspace = true 10 | keywords.workspace = true 11 | categories.workspace = true 12 | readme.workspace = true 13 | 14 | [dependencies] 15 | -------------------------------------------------------------------------------- /aiscript-lexer/src/error_reporter.rs: -------------------------------------------------------------------------------- 1 | use crate::{Token, TokenType}; 2 | 3 | #[derive(Default)] 4 | pub struct ErrorReporter { 5 | pub panic_mode: bool, 6 | pub had_error: bool, 7 | } 8 | 9 | impl ErrorReporter { 10 | pub fn new() -> Self { 11 | Self { 12 | panic_mode: false, 13 | had_error: false, 14 | } 15 | } 16 | 17 | pub fn error_with_line(&mut self, line: u32, message: &str) { 18 | if self.panic_mode { 19 | return; 20 | } 21 | self.panic_mode = true; 22 | self.had_error = true; 23 | eprintln!("[line {}] Error: {}", line, message); 24 | } 25 | 26 | pub fn error_at(&mut self, token: Token<'_>, message: &str) { 27 | if self.panic_mode { 28 | return; 29 | } 30 | self.panic_mode = true; 31 | eprint!("[line {}] Error", token.line); 32 | if token.kind == TokenType::Eof { 33 | eprint!(" at end"); 34 | } else if token.kind == TokenType::Invalid { 35 | // Do nothing. 36 | } else { 37 | eprint!(" at '{}'", token.lexeme); 38 | } 39 | eprintln!(": {message}"); 40 | self.had_error = true; 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /aiscript-runtime/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aiscript-runtime" 3 | description = "The web runtime of AIScript programming language interpreter" 4 | version.workspace = true 5 | edition.workspace = true 6 | homepage.workspace = true 7 | repository.workspace = true 8 | license.workspace = true 9 | authors.workspace = true 10 | keywords.workspace = true 11 | categories.workspace = true 12 | readme.workspace = true 13 | 14 | [dependencies] 15 | aiscript-lexer = { path = "../aiscript-lexer", version = "0.2.0" } 16 | aiscript-common = { path = "../aiscript-common", version = "0.2.0" } 17 | aiscript-directive = { path = "../aiscript-directive", version = "0.2.0" } 18 | aiscript-vm = { path = "../aiscript-vm", version = "0.2.0" } 19 | hyper = "1.6" 20 | hyper-util = "0.1" 21 | tokio = { version = "1.44", features = ["rt-multi-thread", "macros"] } 22 | tower = "0.5" 23 | http-body-util = "0.1" 24 | bytes = "1.10" 25 | axum = "0.8" 26 | axum-extra = { version = "0.10", features = ["typed-header"] } 27 | jsonwebtoken = "9.3" 28 | serde_json.workspace = true 29 | thiserror = "1.0" 30 | indexmap = { version = "2.7", features = ["serde"] } 31 | serde.workspace = true 32 | walkdir = "2.5" 33 | notify = "8.0" 34 | sqlx = { version = "0.8", features = ["runtime-tokio", "postgres"] } 35 | redis.workspace = true 36 | toml = "0.8" 37 | oas3 = "0.15" 38 | reqwest.workspace = true 39 | -------------------------------------------------------------------------------- /aiscript-runtime/src/config/auth.rs: -------------------------------------------------------------------------------- 1 | use serde::Deserialize; 2 | 3 | use aiscript_common::EnvString; 4 | 5 | #[derive(Debug, Deserialize, Default)] 6 | pub struct AuthConfig { 7 | pub jwt: JwtConfig, 8 | pub basic: Option, 9 | } 10 | 11 | #[derive(Debug, Deserialize)] 12 | pub struct JwtConfig { 13 | pub secret: EnvString, 14 | pub expiration: u64, // expiration time in seconds 15 | } 16 | 17 | #[derive(Debug, Deserialize)] 18 | pub struct BasicAuthConfig { 19 | pub username: EnvString, 20 | pub password: EnvString, 21 | } 22 | 23 | impl Default for JwtConfig { 24 | fn default() -> Self { 25 | Self { 26 | secret: EnvString("secret".into()), 27 | expiration: 24 * 3600, 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /aiscript-runtime/src/openapi/redoc.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Redoc 6 | 7 | 8 | 9 | 10 | 16 | 17 | 18 | 19 |
20 | 21 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /aiscript-runtime/src/utils.rs: -------------------------------------------------------------------------------- 1 | #[allow(unused)] 2 | pub fn normalize_path(path: &str) -> String { 3 | let mut result = String::with_capacity(path.len()); 4 | let mut last_was_slash = false; 5 | 6 | for c in path.chars() { 7 | if c == '/' { 8 | if !last_was_slash { 9 | result.push(c); 10 | } 11 | last_was_slash = true; 12 | } else { 13 | result.push(c); 14 | last_was_slash = false; 15 | } 16 | } 17 | 18 | result 19 | } 20 | 21 | #[cfg(test)] 22 | mod tests { 23 | use super::*; 24 | 25 | #[test] 26 | fn test_normalize_path() { 27 | assert_eq!(normalize_path("//path"), "/path"); 28 | assert_eq!(normalize_path("/path///a"), "/path/a"); 29 | assert_eq!(normalize_path("///"), "/"); 30 | assert_eq!(normalize_path("path"), "path"); 31 | assert_eq!(normalize_path("/path/to///file"), "/path/to/file"); 32 | assert_eq!(normalize_path("//path//to////dir/"), "/path/to/dir/"); 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /aiscript-vm/src/builtins/error.rs: -------------------------------------------------------------------------------- 1 | use crate::{ 2 | object::{Class, Object}, 3 | string::InternedString, 4 | value::Value, 5 | vm::Context, 6 | }; 7 | use aiscript_arena::{Gc, GcRefLock, RefLock}; 8 | use std::collections::HashMap; 9 | 10 | pub fn create_validation_error(ctx: Context) -> GcRefLock<'_, Class> { 11 | let error_class = Class::new(ctx.intern(b"ValidationError!")); 12 | Gc::new(&ctx, RefLock::new(error_class)) 13 | } 14 | 15 | // Helper to create error info object 16 | pub fn create_error_info<'gc>( 17 | ctx: Context<'gc>, 18 | field: InternedString<'gc>, 19 | error_type: &str, 20 | message: &str, 21 | input: Value<'gc>, 22 | ) -> Value<'gc> { 23 | let mut fields = HashMap::default(); 24 | fields.insert( 25 | ctx.intern(b"type"), 26 | Value::String(ctx.intern(error_type.as_bytes())), 27 | ); 28 | fields.insert( 29 | ctx.intern(b"loc"), 30 | Value::array(&ctx, vec![Value::String(field)]), 31 | ); 32 | fields.insert( 33 | ctx.intern(b"msg"), 34 | Value::String(ctx.intern(message.as_bytes())), 35 | ); 36 | fields.insert(ctx.intern(b"input"), input); 37 | 38 | Value::Object(Gc::new(&ctx, RefLock::new(Object { fields }))) 39 | } 40 | -------------------------------------------------------------------------------- /aiscript-vm/src/compiler/mod.rs: -------------------------------------------------------------------------------- 1 | use std::collections::BTreeMap; 2 | 3 | use aiscript_arena::Gc; 4 | use codegen::CodeGen; 5 | 6 | use crate::{VmError, ast::ChunkId, object::Function, parser::Parser, vm::Context}; 7 | 8 | mod codegen; 9 | #[cfg(feature = "optimizer")] 10 | mod optimizer; 11 | 12 | pub fn compile<'gc>( 13 | ctx: Context<'gc>, 14 | source: &'gc str, 15 | ) -> Result>>, VmError> { 16 | let mut parser = Parser::new(ctx, source); 17 | let program = parser.parse()?; 18 | #[cfg(feature = "debug")] 19 | println!("AST: {}", program); 20 | #[cfg(feature = "optimizer")] 21 | let optimizer = optimizer::ChunkOptimizer::new(); 22 | 23 | CodeGen::generate(program, ctx).map(|chunks| { 24 | chunks 25 | .into_iter() 26 | .map(|(id, function)| { 27 | #[cfg(feature = "optimizer")] 28 | let mut function = function; 29 | #[cfg(feature = "optimizer")] 30 | optimizer.optimize(&mut function.chunk); 31 | (id, Gc::new(&ctx, function)) 32 | }) 33 | .collect() 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /aiscript-vm/src/stdlib/auth/mod.rs: -------------------------------------------------------------------------------- 1 | mod jwt; 2 | 3 | pub use jwt::create_jwt_module; 4 | -------------------------------------------------------------------------------- /aiscript-vm/src/stdlib/db/mod.rs: -------------------------------------------------------------------------------- 1 | mod pg; 2 | mod redis; 3 | mod sqlite; 4 | 5 | pub use pg::create_pg_module; 6 | pub use redis::create_redis_module; 7 | pub use sqlite::create_sqlite_module; 8 | -------------------------------------------------------------------------------- /aiscript-vm/src/string/mod.rs: -------------------------------------------------------------------------------- 1 | mod interned; 2 | mod utils; 3 | 4 | use aiscript_arena::Gc; 5 | pub use interned::{InternedString, InternedStringSet}; 6 | 7 | use crate::{Value, vm::Context}; 8 | 9 | /// Internal enum to handle string operations 10 | pub enum StringValue<'gc> { 11 | Interned(InternedString<'gc>), 12 | Dynamic(Gc<'gc, String>), 13 | } 14 | 15 | impl<'gc> StringValue<'gc> { 16 | pub fn as_str(&self) -> &str { 17 | match self { 18 | StringValue::Interned(s) => s.to_str().unwrap(), 19 | StringValue::Dynamic(s) => s, 20 | } 21 | } 22 | 23 | // Convert back to Value based on heuristics 24 | pub fn into_value(self, ctx: Context<'gc>, should_intern: bool) -> Value<'gc> { 25 | match (self, should_intern) { 26 | (StringValue::Interned(s), _) => Value::String(s), 27 | (StringValue::Dynamic(s), true) => Value::String(ctx.intern(s.as_bytes())), 28 | (StringValue::Dynamic(s), false) => Value::IoString(s), 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /aiscript-vm/src/vm/fuel.rs: -------------------------------------------------------------------------------- 1 | pub struct Fuel { 2 | value: i32, 3 | } 4 | 5 | impl Fuel { 6 | pub fn new(value: i32) -> Self { 7 | Fuel { value } 8 | } 9 | 10 | /// Add to or subtract from the current remaining fuel. 11 | pub fn adjust(&mut self, fuel: i32) { 12 | self.value = self.value.saturating_add(fuel); 13 | } 14 | 15 | /// Subtract from the current remaining fuel. 16 | /// 17 | /// This is a convenience method that is equivalent to `self.adjust_fuel(-fuel)`. 18 | pub fn consume(&mut self, fuel: i32) { 19 | self.adjust(fuel.saturating_neg()); 20 | } 21 | 22 | /// Returns true if we have positive fuel remaining. 23 | pub fn should_continue(&self) -> bool { 24 | self.value > 0 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /aiscript/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "aiscript" 3 | description = "The AIScript programming language" 4 | version.workspace = true 5 | edition.workspace = true 6 | homepage.workspace = true 7 | repository.workspace = true 8 | license.workspace = true 9 | keywords.workspace = true 10 | categories.workspace = true 11 | readme.workspace = true 12 | default-run = "aiscript" 13 | 14 | [[bin]] 15 | name = "aiscript" 16 | path = "src/main.rs" 17 | 18 | [dependencies] 19 | aiscript-vm = { path = "../aiscript-vm", version = "0.2.0" } 20 | aiscript-runtime = { path = "../aiscript-runtime", version = "0.2.0" } 21 | clap = { version = "4.5", features = ["derive"] } 22 | tokio = { version = "1.44", features = ["rt-multi-thread", "macros"] } 23 | dotenv = "0.15.0" 24 | rustyline = "15.0" 25 | dirs = "6.0" 26 | serde.workspace = true 27 | whoami = "1.4.1" 28 | 29 | [dev-dependencies] 30 | tempfile = "3.8.1" 31 | 32 | [features] 33 | ai_test = ["aiscript-vm/ai_test"] 34 | -------------------------------------------------------------------------------- /assets/toucan.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aiscriptdev/aiscript/d0aab785ff719238e37274dc200658a8b4b596af/assets/toucan.png -------------------------------------------------------------------------------- /dist-workspace.toml: -------------------------------------------------------------------------------- 1 | [workspace] 2 | members = ["cargo:."] 3 | 4 | # Config for 'dist' 5 | [dist] 6 | # The preferred dist version to use in CI (Cargo.toml SemVer syntax) 7 | cargo-dist-version = "0.28.0" 8 | # CI backends to support 9 | ci = "github" 10 | # The installers to generate for each app 11 | installers = ["shell", "powershell"] 12 | # Target platforms to build apps for (Rust target-triple syntax) 13 | # Disable aarch64-unknown-linux-gnu temporary since it build failed due to lack OpenSSL dependency 14 | targets = ["aarch64-apple-darwin", "x86_64-apple-darwin", "x86_64-unknown-linux-gnu", "x86_64-pc-windows-msvc"] 15 | # Path that installers should place binaries in 16 | install-path = "CARGO_HOME" 17 | # Whether to install an updater program 18 | install-updater = false 19 | -------------------------------------------------------------------------------- /examples/array.ai: -------------------------------------------------------------------------------- 1 | let arr = [1,2,3,4]; 2 | let result = arr |> map(|x| x + 1) |> filter(|x| x % 2 == 0); 3 | print(result); // expect: [2, 4] -------------------------------------------------------------------------------- /examples/claude.ai: -------------------------------------------------------------------------------- 1 | let a = prompt { 2 | input: "What is rust?", 3 | model: "claude-3-7-sonnet-latest" 4 | }; 5 | print(a); -------------------------------------------------------------------------------- /examples/env.ai: -------------------------------------------------------------------------------- 1 | print($HOME); // Prints home directory 2 | let h = "HOME"; 3 | print($h); // Also prints home directory 4 | print($NONEXISTENT); // Prints empty string -------------------------------------------------------------------------------- /examples/error_handle.ai: -------------------------------------------------------------------------------- 1 | // class FileNotFound! { 2 | // name: str, 3 | // } 4 | // print(FileNotFound!); 5 | 6 | enum IOError! { 7 | DiskFull = "the disk is fulled", 8 | Interrupted = "io is interrupted", 9 | } 10 | // print(IOError!); 11 | 12 | // fn check_error(e: IOError!) { 13 | // print(e); 14 | // } 15 | 16 | // check_error(IOError!::DiskFull); 17 | 18 | enum ArithError! { 19 | DivideZero, 20 | } 21 | 22 | fn divide(a, b) -> int | ArithError! { 23 | if b == 0 { 24 | raise ArithError!::DivideZero; 25 | } 26 | 27 | return a / b; 28 | } 29 | 30 | fn do_math() { 31 | let x = 0; 32 | let v = divide(1, 0) |err| { 33 | let a = 1; 34 | print("error:", err); 35 | 999 36 | }; 37 | print(v); 38 | // let y = divide(1, 1)?; 39 | // print(y); 40 | } 41 | 42 | let x = do_math(); 43 | print(x); 44 | // { 45 | // print(1); 46 | // let a = IOError!::DiskFull; 47 | // let x = a; 48 | // } -------------------------------------------------------------------------------- /examples/lambda.ai: -------------------------------------------------------------------------------- 1 | let x = 2; 2 | if x > 10 { 3 | print("big"); 4 | } else if x > 5 { 5 | print("medium"); 6 | } else { 7 | print("small"); 8 | } 9 | 10 | let obj = {x: 1, y: 2}; 11 | print(obj); 12 | 13 | if true { 14 | print(true); 15 | } 16 | if true { 17 | "ok"; 18 | } else { 19 | fn foo() {} 20 | } 21 | print({arr:[1]} == [{"x":1}]); 22 | 23 | if 0 { print(0); } 24 | 25 | while x > 0 { 26 | print(x); 27 | x = x - 1; 28 | } 29 | 30 | while len([1,2,3]) > 0 { // Error due to in_flow_condition check 31 | // ... 32 | print("yes"); 33 | break; 34 | } 35 | 36 | // Basic for loop 37 | for let i = 0; i < 10; i = i + 1 { 38 | print(i); 39 | } 40 | 41 | let i = 8; 42 | // Loop without initializer 43 | for ; i < 10; i = i + 1 { 44 | print(i); 45 | } 46 | 47 | // Loop without increment 48 | for let i = 0; i < 10; { 49 | print(i); 50 | i = i + 1; 51 | } 52 | -------------------------------------------------------------------------------- /examples/main.ai: -------------------------------------------------------------------------------- 1 | let p = "What is the moon of Pluto?"; 2 | ai fn ask(question) { 3 | return prompt question; 4 | } 5 | 6 | let answer = ask(p); 7 | print(answer); -------------------------------------------------------------------------------- /examples/ollama.ai: -------------------------------------------------------------------------------- 1 | let a = prompt { 2 | input: "What is rust?", 3 | model: "llama3.2" 4 | }; 5 | print(a); -------------------------------------------------------------------------------- /examples/project.toml: -------------------------------------------------------------------------------- 1 | [network] 2 | host = "0.0.0.0" 3 | port = 5042 4 | 5 | [apidoc] 6 | enabled = true 7 | type = "swagger" 8 | path = "/docs" 9 | 10 | [database.postgresql] 11 | url = "$DATABASE_URL" 12 | 13 | [auth.jwt] 14 | secret = "$JWT_SECRET" 15 | expiration = 3600 16 | 17 | [auth.basic] 18 | username = "admin" 19 | password = "123456" 20 | 21 | [sso.google] 22 | client_id = "123" 23 | client_secret = "abc" 24 | redirect_url = "http://localhost:8080/callback" 25 | scopes = ["email"] 26 | 27 | [ai.anthropic] 28 | api_key = "$CLAUDE_API_KEY" 29 | model = "claude-3-5-sonnet-latest" 30 | 31 | [ai.ollama] 32 | api_endpoint = "http://localhost:11434/v1" 33 | model = "llama3.2" 34 | -------------------------------------------------------------------------------- /examples/redis.ai: -------------------------------------------------------------------------------- 1 | use std.db.redis; 2 | 3 | // Simple commands 4 | redis.cmd("SET mykey somevalue"); 5 | let value = redis.cmd("GET mykey"); 6 | print(value); 7 | let lists = redis.cmd("LRANGE mylist 0 -1"); 8 | print(lists); 9 | 10 | 11 | // Pipeline usage 12 | let p = redis.pipeline(); 13 | p.cmd("SET k1 v1"); 14 | p.cmd("SET k2 v2"); 15 | p.cmd("GET k1"); 16 | let results = p.exec(); // Execute pipeline 17 | print(results); // ["OK", "OK", "v1"] 18 | // or 19 | // p.discard(); // Discard pipeline -------------------------------------------------------------------------------- /examples/routes/ai.ai: -------------------------------------------------------------------------------- 1 | @docs(tag="Guess", deprecated=true) 2 | get /guess { 3 | 4 | query { 5 | @number(min=0, max=100) 6 | value: int 7 | } 8 | 9 | let message = "You got it!" if query.value == 42 else "Try again"; 10 | return { message }; 11 | } 12 | 13 | get /hello { 14 | query { 15 | name: str 16 | } 17 | 18 | return { message: f"Hello, {query.name}!" }; 19 | } -------------------------------------------------------------------------------- /examples/routes/auth.ai: -------------------------------------------------------------------------------- 1 | route / { 2 | @auth // JWT auth 3 | get /ask { 4 | query { 5 | @string(min_len=5, max_len=50) 6 | question: str 7 | } 8 | 9 | ai fn ask_llm(question: str) -> str { 10 | return prompt question; 11 | } 12 | // http.get("https://api.openai.com/v1/engines/davinci/completions"); 13 | let answer = ask_llm(query.question); 14 | return answer; 15 | } 16 | 17 | @basic_auth // Username/Password auth 18 | post /guess { 19 | body { 20 | @number(min=1, max=100) 21 | magic: int = 1 22 | } 23 | 24 | header.path = "/guess"; 25 | let msg = "Try again!"; 26 | if body.magic == 42 { 27 | msg = "You guessed it!"; 28 | } 29 | return { 30 | body: msg, 31 | headers: { 32 | path: "/guess", 33 | } 34 | }; 35 | } 36 | 37 | @sso(provider="google") 38 | get /auth/google { 39 | let url = sso.authority_url(); 40 | print(url); 41 | return temporary_redirect(target=url); 42 | } 43 | 44 | get /header { 45 | return response( 46 | status_code=404, 47 | body={ 48 | "message": "Hello, world!", 49 | }, 50 | headers= { 51 | "X-Header": "Hello, world!", 52 | }); 53 | } 54 | } 55 | 56 | 57 | 58 | 59 | 60 | -------------------------------------------------------------------------------- /examples/routes/nested/gist.ai: -------------------------------------------------------------------------------- 1 | get /gists { 2 | """Gist lists""" 3 | return "gist"; 4 | } 5 | 6 | get /gists/:id { 7 | """Get gist by id""" 8 | path { 9 | id: str, 10 | } 11 | return "gist: " + path.id; 12 | } 13 | 14 | delete /gists/:id { 15 | """Delete gist by id""" 16 | path { 17 | id: str, 18 | } 19 | return "delete gist: " + path.id; 20 | } -------------------------------------------------------------------------------- /examples/routes/path.ai: -------------------------------------------------------------------------------- 1 | get /users/:id/posts/:postId { 2 | path { 3 | """User Id""" 4 | @string(min_len=3) 5 | id: str, 6 | """Post Id""" 7 | postId: int, 8 | } 9 | 10 | query { 11 | refresh: bool = true 12 | } 13 | 14 | print(path); 15 | let userId = path.id; 16 | let postId = path.postId; 17 | return f"Accessing post {postId} for user {userId}"; 18 | } -------------------------------------------------------------------------------- /examples/routes/poc.ai: -------------------------------------------------------------------------------- 1 | route /poc { 2 | """Test POC API""" 3 | get /hello { 4 | """Get hello API""" 5 | query { 6 | """hello content""" 7 | @string(min_len=3, max_len=30) 8 | content: str, 9 | """hello age""" 10 | age: int = 18, 11 | } 12 | 13 | print(query); 14 | print(body); 15 | print(request); 16 | print(header); 17 | print(query.content); 18 | print(query.age + 2); 19 | let name = "aiscript"; 20 | print("name: " + name); 21 | return "get: hello"; 22 | } 23 | 24 | post /hello, put /hello2 { 25 | """Post and put hello API""" 26 | query { 27 | @string(min_len=3, max_len=30) 28 | content: str, 29 | """age""" 30 | age: int = 18, 31 | @in(["ios", "android"]) 32 | platform: str = "ios", 33 | } 34 | 35 | @json 36 | body { 37 | """test flag""" 38 | test: bool = true, 39 | } 40 | 41 | ai fn ask(question) { 42 | return prompt question; 43 | } 44 | 45 | let answer = ask(query.content); 46 | return "AI answer: " + answer; 47 | } 48 | } -------------------------------------------------------------------------------- /examples/routes/regex.ai: -------------------------------------------------------------------------------- 1 | post /api/register { 2 | @json 3 | body { 4 | """Username validation (alphanumeric and underscore only)""" 5 | @regex(pattern="^[a-zA-Z0-9_]+$") 6 | username: str, 7 | 8 | """SSN validation (XXX-XX-XXXX format)""" 9 | @regex(pattern="^\d{3}-\d{2}-\d{4}$") 10 | ssn: str 11 | } 12 | 13 | print("Received registration for: ", username); 14 | return { "success": true }; 15 | } 16 | 17 | get /api/validate-phone { 18 | query { 19 | """Phone number validation (XXX-XXX-XXXX format)""" 20 | @regex(pattern="^\d{3}-\d{3}-\d{4}$") 21 | phone: str 22 | } 23 | 24 | return { "valid": true }; 25 | } 26 | -------------------------------------------------------------------------------- /examples/routes/repo.ai: -------------------------------------------------------------------------------- 1 | get /repo { 2 | """Repo API""" 3 | query { 4 | """repo name""" 5 | @string(min_len=3, max_len=30) 6 | name: str 7 | } 8 | 9 | return "repo: " + name; 10 | } 11 | 12 | post /repo { 13 | """Create a repository""" 14 | body { 15 | """The repository name""" 16 | @string(min_len=3, max_len=30) 17 | name: str 18 | } 19 | 20 | return "create repo: " + name; 21 | } 22 | 23 | put /repo/:id { 24 | """Update a repository API""" 25 | path { 26 | id: str 27 | } 28 | body { 29 | """The repository name""" 30 | @string(min_len=3, max_len=30) 31 | name: str 32 | } 33 | 34 | return "update repo: " + name; 35 | } 36 | -------------------------------------------------------------------------------- /examples/serde.ai: -------------------------------------------------------------------------------- 1 | use std.serde; 2 | let s = "{\"name\": \"Alice\", \"age\": 30}"; 3 | let obj = serde.from_str(s); 4 | // Basic usage without pretty-printing 5 | let str1 = serde.to_str(obj); 6 | print(str1); 7 | // With pretty-printing 8 | let str2 = serde.to_str(obj, pretty=true); 9 | print(str2); 10 | 11 | // File operations with pretty-printing 12 | serde.to_file("output.json", obj, pretty=true); -------------------------------------------------------------------------------- /examples/sql.ai: -------------------------------------------------------------------------------- 1 | use std.db.pg; 2 | 3 | pg.query("INSERT INTO language (name) VALUES($1) RETURNING id, name;", "Rust"); 4 | let languages = pg.query("select id, name from language;"); 5 | print(languages); // expect: [1, "Rust"] 6 | let languages = pg.query_as(Language, "select id, name from language where id > 3 limit 10;"); 7 | class Language { 8 | id: int, 9 | name: str, 10 | } 11 | print(languages); 12 | 13 | let tx = pg.begin_transaction(); 14 | print(tx); 15 | let r1 = tx.query("INSERT INTO language (name) VALUES($1) RETURNING id, name;", "JavaScript"); 16 | print(r1); 17 | let r2 = tx.query("INSERT INTO language (name) VALUES($1) RETURNING id, name;", "AIScript"); 18 | print(r2); 19 | let languages = pg.query_as(Language, "select id, name from language where id > 10 limit 10;"); 20 | print(languages); 21 | tx.commit(); 22 | 23 | // print(pg.query(r"update language set "name"='aiscript22' where id=38 returning id, name;")); -------------------------------------------------------------------------------- /examples/test.ai: -------------------------------------------------------------------------------- 1 | use std.math; 2 | use std.io; 3 | use std.time; 4 | use std.str; 5 | use user_math; 6 | 7 | print(str.len("abcd")); 8 | print(str.to_uppercase("abcd")); 9 | print(time.now()); 10 | print(math.add(1, 2)); // expect: 3 11 | print(math.sub(1, 1)); // expect: 0 12 | 13 | print(math.PI); // expect: 3.14 14 | 15 | print(math.sqrt(16)); // 4 16 | 17 | print(user_math.add(1,2)); 18 | print(user_math.PI); 19 | 20 | let content = io.read_file("main.ai"); 21 | print(content); 22 | let lines = io.read_lines("test.ai"); 23 | print(lines); 24 | 25 | print(io.is_file("a")); 26 | let arr = [1,2,3]; 27 | print(sum(arr)); -------------------------------------------------------------------------------- /examples/tuple.ai: -------------------------------------------------------------------------------- 1 | let a = (1,2,3); 2 | print(a[0]); 3 | a[0] = 11; 4 | print(a[0]); 5 | -------------------------------------------------------------------------------- /examples/user_math.ai: -------------------------------------------------------------------------------- 1 | print("loaded math.ai"); 2 | pub const PI = 3.14; 3 | pub fn add(x: int, y: int) -> int { // Available as math.add 4 | return x + y; 5 | } 6 | 7 | pub fn sub(x: int, y: int) -> int { // Available as math.add 8 | return x - y; 9 | } 10 | 11 | print(add(1,2)); 12 | print(PI); 13 | 14 | pub class A { 15 | fn test() { 16 | print("test from A"); 17 | } 18 | } 19 | print("end of math.ai"); 20 | 21 | 22 | let global_variable = "abc"; 23 | 24 | if len(global_variable) > 1 { 25 | let local_variable = "xyz"; 26 | print("I can access global variable:", global_variable); 27 | } 28 | 29 | // local variable is inaccessible outer of local scope, 30 | // this print statement would 31 | // print(local_variable); 32 | 33 | const PI = 3.14; 34 | 35 | print(PI); // print 3.14 36 | PI = 3.1415; // -------------------------------------------------------------------------------- /examples/validation.ai: -------------------------------------------------------------------------------- 1 | class User { 2 | @string(min_len=3, max=20) 3 | name: str, 4 | @number(min=18, strict_int=true, strict_float=true) 5 | age: int, 6 | @in(["male", "female"]) 7 | gender: str = "male", 8 | } 9 | let u1 = User("Le", 20.0, "male") |err| { 10 | print("Validate error:", err); 11 | }; 12 | // Validate error: { 13 | // loc: [name], input: Le, type: validation_error, 14 | // msg: String length is less than the minimum length of 3 15 | // } 16 | let u2 = User("Lee", 17, "male") |err| { 17 | print("Validate error:", err); 18 | }; 19 | // Validate error: { 20 | // loc: [age], input: 17, type: validation_error, 21 | // msg: Number is less than the minimum value of 18 22 | // } 23 | let u3 = User("Lee", 20, "boy") |err| { 24 | print("Validate error:", err); 25 | }; 26 | // Validate error: { 27 | // loc: [gender], input: boy, type: validation_error, 28 | // msg: Value is not in the list of allowed values 29 | // } 30 | let u4 = User("Lee") |err| { 31 | print("Validate error:", err); 32 | }; 33 | // Validate error: { 34 | // loc: [age], input: nil, type: missing, 35 | // msg: Field required 36 | // } 37 | 38 | 39 | -------------------------------------------------------------------------------- /tests/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "tests" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | repository.workspace = true 6 | license.workspace = true 7 | publish = false 8 | 9 | [[test]] 10 | name = "integration" 11 | path = "integration.rs" 12 | 13 | [dependencies] 14 | regex = "1.11" 15 | test-generator = "0.3" 16 | 17 | [dev-dependencies] 18 | -------------------------------------------------------------------------------- /tests/integration/ai/ai_function.ai: -------------------------------------------------------------------------------- 1 | ai fn f1() { 2 | return prompt "What is the moon of Pluto?"; 3 | } 4 | 5 | print(f1()); // expect: AI: What is the moon of Pluto? 6 | -------------------------------------------------------------------------------- /tests/integration/ai/ai_function_keyword.ai: -------------------------------------------------------------------------------- 1 | ai f2() {} // Error at 'f2': Expect 'fn' after 'ai'. 2 | -------------------------------------------------------------------------------- /tests/integration/ai/ai_function_missing.ai: -------------------------------------------------------------------------------- 1 | fn f2() { 2 | prompt "1 + 1 = ?"; // Error at 'prompt': Can't prompt outside of ai function or root script. 3 | } -------------------------------------------------------------------------------- /tests/integration/ai/prompt.ai: -------------------------------------------------------------------------------- 1 | let p = "What is AIScript?"; 2 | let a = prompt p; 3 | print(a); // expect: AI: What is AIScript? 4 | -------------------------------------------------------------------------------- /tests/integration/ai/unsupported_model.ai: -------------------------------------------------------------------------------- 1 | // ignore 2 | let a = prompt { 3 | input: "hi", 4 | model: "invalid-model", 5 | }; 6 | // expect runtime error: Unsupported model 'invalid-model'. 7 | -------------------------------------------------------------------------------- /tests/integration/array/equality.ai: -------------------------------------------------------------------------------- 1 | // Basic array equality 2 | print([] == []); // expect: true 3 | print([] != []); // expect: false 4 | print([] == nil); // expect: false 5 | print([] != nil); // expect: true 6 | 7 | // Compare arrays with elements 8 | print([1,2] == [1,2]); // expect: true 9 | print([1,2] != [1,2]); // expect: false 10 | print([1] == [2]); // expect: false 11 | 12 | // Same array reference 13 | let a1 = [1,2]; 14 | print(a1 == a1); // expect: true 15 | a1[2] = 3; 16 | print(a1 == a1); // expect: true 17 | 18 | // Different array references 19 | let a2 = [1,2]; 20 | let a3 = [1,2]; 21 | print(a2 == a3); // expect: true 22 | print(a2 != a3); // expect: false 23 | 24 | // Nested arrays 25 | print([[1,2],[3,4]] == [[1,2],[3,4]]); // expect: true 26 | let m1 = [[1,2]]; 27 | let m2 = [[1,2]]; 28 | print(m1 == m2); // expect: true 29 | print(m1[0] == m2[0]); // expect: true 30 | 31 | // Arrays in objects 32 | let obj = {arr: [1,2]}; 33 | let obj2 = {arr: [1,2]}; 34 | print(obj.arr == obj2.arr); // expect: true 35 | 36 | // Object with array and array with object 37 | print({arr:[1]} == [{"x":1}]); // expect: false 38 | -------------------------------------------------------------------------------- /tests/integration/array/invalid_syntax.ai: -------------------------------------------------------------------------------- 1 | // Invalid array literal syntax 2 | let a1 = [ 3 | "value" "bad" // Error at 'bad': Expect ',' after array element. 4 | ]; 5 | 6 | let a2 = [ 7 | 1 2 3 // Error at '2': Expect ',' after array element. 8 | ]; 9 | 10 | // Invalid array access 11 | let arr = [1,2]; 12 | print(arr[]); // Error at ']': Expect expression. 13 | -------------------------------------------------------------------------------- /tests/integration/array/normal.ai: -------------------------------------------------------------------------------- 1 | // Basic array literal 2 | let arr = [1, 2, 3]; 3 | print(arr[0]); // expect: 1 4 | 5 | // Mixed types array 6 | let mixed = [1, "two", true, nil]; 7 | print(mixed[1]); // expect: two 8 | print(mixed[3]); // expect: nil 9 | 10 | // Array indexing 11 | let i = 1; 12 | let arr2 = [10, 20, 30]; 13 | print(arr2[i]); // expect: 20 14 | 15 | // Array modification 16 | let nums = [1, 2]; 17 | nums[2] = 3; 18 | nums[0] = 10; 19 | print(nums); // expect: [10, 2, 3] 20 | 21 | // Nested arrays 22 | let matrix = [ 23 | [1, 2], 24 | [3, 4], // trailing comma 25 | ]; 26 | print(matrix[0][1]); // expect: 2 27 | matrix[1][0] = 5; 28 | print(matrix[1]); // expect: [5, 4] 29 | 30 | // Arrays in objects 31 | let obj = { 32 | numbers: [1, 2, 3], 33 | strings: ["a", "b"], 34 | }; 35 | print(obj.numbers[0]); // expect: 1 36 | obj.strings[1] = "c"; 37 | print(obj.strings); // expect: [a, c] -------------------------------------------------------------------------------- /tests/integration/array/single_element.ai: -------------------------------------------------------------------------------- 1 | // Test single element array, since enum variant evaluate has the similar syntax 2 | print([1]); // expect: [1] 3 | print([1,]); // expect: [1] 4 | print([1 + 1]); // expect: [2] 5 | print([(1)]); // expect: [1] 6 | print([[1]]); // expect: [[1]] 7 | print(["a"]); // expect: [a] 8 | print(["a",]); // expect: [a] 9 | print([true]); // expect: [true] 10 | print([true,]); // expect: [true] 11 | 12 | let x = "x"; 13 | // must append , for variable element, 14 | // to avoid syntax conflict with variant evaluation 15 | print([x,]); // expect: [x] 16 | print([(x)]); // expect: [x] 17 | print([x + "y"]); // expect: [xy] -------------------------------------------------------------------------------- /tests/integration/assignment/associativity.ai: -------------------------------------------------------------------------------- 1 | let a = "a"; 2 | let b = "b"; 3 | let c = "c"; 4 | 5 | // Assignment is right-associative. 6 | a = b = c; 7 | print(a); // expect: c 8 | print(b); // expect: c 9 | print(c); // expect: c 10 | -------------------------------------------------------------------------------- /tests/integration/assignment/global.ai: -------------------------------------------------------------------------------- 1 | let a = "before"; 2 | print(a); // expect: before 3 | 4 | a = "after"; 5 | print(a); // expect: after 6 | 7 | a = "arg"; 8 | print(a); // expect: arg 9 | -------------------------------------------------------------------------------- /tests/integration/assignment/grouping.ai: -------------------------------------------------------------------------------- 1 | let a = "a"; 2 | (a) = "value"; // Error at '=': Invalid assignment target. 3 | -------------------------------------------------------------------------------- /tests/integration/assignment/infix_operator.ai: -------------------------------------------------------------------------------- 1 | let a = "a"; 2 | let b = "b"; 3 | a + b = "value"; // Error at '=': Invalid assignment target. 4 | -------------------------------------------------------------------------------- /tests/integration/assignment/local.ai: -------------------------------------------------------------------------------- 1 | { 2 | let a = "before"; 3 | print(a); // expect: before 4 | 5 | a = "after"; 6 | print(a); // expect: after 7 | 8 | a = "arg"; 9 | print(a); // expect: arg 10 | } 11 | -------------------------------------------------------------------------------- /tests/integration/assignment/prefix_operator.ai: -------------------------------------------------------------------------------- 1 | let a = "a"; 2 | -a = "value"; // Error at '=': Invalid assignment target. 3 | -------------------------------------------------------------------------------- /tests/integration/assignment/syntax.ai: -------------------------------------------------------------------------------- 1 | // Assignment on RHS of variable. 2 | let a = "before"; 3 | let c = a = "let"; 4 | print(a); // expect: let 5 | print(c); // expect: let 6 | -------------------------------------------------------------------------------- /tests/integration/assignment/to_self.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn new() { 3 | self = "value"; // Error at '=': Invalid assignment target. 4 | } 5 | } 6 | 7 | Foo(); 8 | -------------------------------------------------------------------------------- /tests/integration/assignment/undefined.ai: -------------------------------------------------------------------------------- 1 | unknown = "what"; // expect runtime error: Undefined variable 'unknown'. 2 | -------------------------------------------------------------------------------- /tests/integration/block/empty.ai: -------------------------------------------------------------------------------- 1 | {} // By itself. 2 | 3 | // In a statement. 4 | if true {} 5 | if false {} else {} 6 | 7 | print("ok"); // expect: ok 8 | -------------------------------------------------------------------------------- /tests/integration/block/scope.ai: -------------------------------------------------------------------------------- 1 | let a = "outer"; 2 | 3 | { 4 | let a = "inner"; 5 | print(a); // expect: inner 6 | } 7 | 8 | print(a); // expect: outer 9 | -------------------------------------------------------------------------------- /tests/integration/bool/equality.ai: -------------------------------------------------------------------------------- 1 | print(true == true); // expect: true 2 | print(true == false); // expect: false 3 | print(false == true); // expect: false 4 | print(false == false); // expect: true 5 | 6 | // Not equal to other types. 7 | print(true == 1); // expect: false 8 | print(false == 0); // expect: false 9 | print(true == "true"); // expect: false 10 | print(false == "false"); // expect: false 11 | print(false == ""); // expect: false 12 | 13 | print(true != true); // expect: false 14 | print(true != false); // expect: true 15 | print(false != true); // expect: true 16 | print(false != false); // expect: false 17 | 18 | // Not equal to other types. 19 | print(true != 1); // expect: true 20 | print(false != 0); // expect: true 21 | print(true != "true"); // expect: true 22 | print(false != "false"); // expect: true 23 | print(false != ""); // expect: true 24 | -------------------------------------------------------------------------------- /tests/integration/bool/not.ai: -------------------------------------------------------------------------------- 1 | print(not true); // expect: false 2 | print(not false); // expect: true 3 | print(not not true); // expect: true 4 | -------------------------------------------------------------------------------- /tests/integration/break_continue/break_syntax.ai: -------------------------------------------------------------------------------- 1 | let x = 1; 2 | while x <= 12 { 3 | x = x + 1; 4 | if x > 5 { break; } 5 | print(x); 6 | } 7 | 8 | // expect: 2 9 | // expect: 3 10 | // expect: 4 11 | // expect: 5 12 | 13 | for let i = 0; i < 10; i = i + 1 { 14 | if i == 5 { break; } 15 | print(i); 16 | } 17 | 18 | // expect: 0 19 | // expect: 1 20 | // expect: 2 21 | // expect: 3 22 | // expect: 4 -------------------------------------------------------------------------------- /tests/integration/break_continue/continue_syntax.ai: -------------------------------------------------------------------------------- 1 | let x = 1; 2 | while x <= 12 { 3 | x = x + 1; 4 | if x < 10 { continue; } 5 | print(x); 6 | } 7 | 8 | // expect: 10 9 | // expect: 11 10 | // expect: 12 11 | // expect: 13 12 | 13 | for let i = 0; i < 10; i = i + 1 { 14 | if i == 2 { continue; } 15 | print(i); 16 | } 17 | 18 | // expect: 0 19 | // expect: 1 20 | // expect: 3 21 | // expect: 4 22 | // expect: 5 23 | // expect: 6 24 | // expect: 7 25 | // expect: 8 26 | // expect: 9 -------------------------------------------------------------------------------- /tests/integration/break_continue/outside_loop.ai: -------------------------------------------------------------------------------- 1 | if true { 2 | break; // Error at 'break': Can't use 'break' outside of a loop. 3 | } 4 | 5 | break; // Error at 'break': Can't use 'break' outside of a loop. 6 | 7 | if true { 8 | continue; // Error at 'continue': Can't use 'continue' outside of a loop. 9 | } 10 | 11 | continue; // Error at 'continue': Can't use 'continue' outside of a loop. 12 | 13 | -------------------------------------------------------------------------------- /tests/integration/builtin_functions/ascii_chr_ord.ai: -------------------------------------------------------------------------------- 1 | print(ascii("hello")); // expect: hello 2 | print(ascii("Hello, World!")); // expect: Hello, World! 3 | print(ascii("résumé")); // expect: r\xe9sum\xe9 4 | print(ascii("")); // expect: 5 | print(ascii("🙂")); // expect: \x1f642 6 | 7 | print(chr(65)); // expect: A 8 | print(chr(97)); // expect: a 9 | print(chr(32)); // expect: 10 | // print(chr(0)); // _expect: \0 11 | // print(chr(128)); // _expect: \\u{80} 12 | print(chr(960)); // expect: π 13 | 14 | print(ord("A")); // expect: 65 15 | print(ord("a")); // expect: 97 16 | print(ord(" ")); // expect: 32 17 | print(ord("π")); // expect: 960 18 | // print(ord("\n")); // _expect: 10 19 | -------------------------------------------------------------------------------- /tests/integration/builtin_functions/bin_hex_oct.ai: -------------------------------------------------------------------------------- 1 | print(bin(0)); // expect: 0b0 2 | print(bin(1)); // expect: 0b1 3 | print(bin(2)); // expect: 0b10 4 | print(bin(10)); // expect: 0b1010 5 | print(bin(-1)); // expect: -0b1 6 | print(bin(-10)); // expect: -0b1010 7 | print(bin(255)); // expect: 0b11111111 8 | 9 | print(hex(0)); // expect: 0x0 10 | print(hex(1)); // expect: 0x1 11 | print(hex(10)); // expect: 0xa 12 | print(hex(16)); // expect: 0x10 13 | print(hex(-1)); // expect: -0x1 14 | print(hex(-16)); // expect: -0x10 15 | print(hex(255)); // expect: 0xff 16 | print(hex(4095)); // expect: 0xfff 17 | 18 | print(oct(0)); // expect: 0o0 19 | print(oct(1)); // expect: 0o1 20 | print(oct(8)); // expect: 0o10 21 | print(oct(9)); // expect: 0o11 22 | print(oct(-8)); // expect: -0o10 23 | print(oct(64)); // expect: 0o100 24 | print(oct(511)); // expect: 0o777 25 | -------------------------------------------------------------------------------- /tests/integration/builtin_functions/bool.ai: -------------------------------------------------------------------------------- 1 | // Boolean values 2 | print(bool(true)); // expect: true 3 | print(bool(false)); // expect: false 4 | 5 | // Numbers 6 | print(bool(0)); // expect: false 7 | print(bool(0.0)); // expect: false 8 | print(bool(-0.0)); // expect: false 9 | print(bool(1)); // expect: true 10 | print(bool(-1)); // expect: true 11 | print(bool(0.1)); // expect: true 12 | print(bool(float(0))); // expect: false 13 | 14 | // Strings 15 | print(bool("")); // expect: false 16 | print(bool("hello")); // expect: true 17 | print(bool(" ")); // expect: true 18 | 19 | // Nil 20 | print(bool(nil)); // expect: false 21 | 22 | // Collections 23 | print(bool([])); // expect: false 24 | print(bool([1, 2])); // expect: true 25 | print(bool({})); // expect: false 26 | print(bool({x: 1})); // expect: true 27 | 28 | // Functions and classes 29 | fn test() {} 30 | print(bool(test)); // expect: true 31 | class Test {} 32 | print(bool(Test)); // expect: true 33 | print(bool(Test())); // expect: true 34 | -------------------------------------------------------------------------------- /tests/integration/builtin_functions/callable.ai: -------------------------------------------------------------------------------- 1 | // Test with functions 2 | fn test_fn() { 3 | print("test"); 4 | } 5 | print(callable(test_fn)); // expect: true 6 | print(callable(print)); // expect: true 7 | 8 | // Test with classes 9 | class TestClass { 10 | fn new() {} 11 | } 12 | print(callable(TestClass)); // expect: true 13 | 14 | // Test with methods 15 | let instance = TestClass(); 16 | print(callable(instance.new)); // expect: true 17 | 18 | // Test with non-callable types 19 | print(callable(1)); // expect: false 20 | print(callable("string")); // expect: false 21 | print(callable(true)); // expect: false 22 | print(callable(nil)); // expect: false 23 | print(callable([1, 2, 3])); // expect: false 24 | -------------------------------------------------------------------------------- /tests/integration/builtin_functions/filter.ai: -------------------------------------------------------------------------------- 1 | let nums = [1, 2, 3, 4, 5, 6]; 2 | print(filter(nums, |x| x % 2 == 0)); // expect: [2, 4, 6] 3 | fn is_even(x) { 4 | return x % 2 == 0; 5 | } 6 | print(filter(nums, is_even)); // expect: [2, 4, 6] 7 | 8 | let mixed = [-2, -1, 0, 1, 2]; 9 | print(filter(mixed, |x| x > 0)); // expect: [1, 2] 10 | let is_positive = |x| x > 0; 11 | print(filter(mixed, is_positive)); // expect: [1, 2] 12 | 13 | let nums2 = [1, 2, 3]; 14 | print(filter(nums2, |x| x > 10)); // expect: [] 15 | 16 | let empty = []; 17 | print(filter(empty, |x| true)); // expect: [] 18 | 19 | let words = ["hello", "", "world", "", "!"]; 20 | print(filter(words, |s| s != "")); // expect: [hello, world, !] -------------------------------------------------------------------------------- /tests/integration/builtin_functions/float.ai: -------------------------------------------------------------------------------- 1 | print(float(1)); // expect: 1 2 | print(float(0)); // expect: 0 3 | print(float(-1)); // expect: -1 4 | print(float("1.5")); // expect: 1.5 5 | print(float("-2.5")); // expect: -2.5 6 | print(float("0")); // expect: 0 7 | print(float(true)); // expect: 1 8 | print(float(false)); // expect: 0 9 | print(float(nil)); // expect: 0 -------------------------------------------------------------------------------- /tests/integration/builtin_functions/format.ai: -------------------------------------------------------------------------------- 1 | // String formatting 2 | let s = format("{:>10}", "right"); // expect: " right" 3 | print(s); 4 | 5 | // Floating point formatting 6 | print(format("{:.2f}", 3.14159)); // expect: 3.14 7 | print(format("{:x}", 255.0)); // expect: ff 8 | print(format("{:X}", 255.0)); // expect: FF 9 | print(format("{:b}", 42.0)); // expect: 101010 10 | 11 | // Integer formatting 12 | print(format("{:o}", 64)); // expect: 100 13 | print(format("{:x}", 255)); // expect: ff 14 | print(format("{:b}", 42)); // expect: 101010 15 | // Regular decimal formatting 16 | print(format("{:d}", 123.45)); // expect: 123 17 | 18 | // Width and alignment still work with all formats 19 | print(format("{:>10x}", 255.0)); // expect: " ff" 20 | print(format("{:0>8b}", 42.0)); // expect: 00101010 21 | 22 | // Combining both 23 | print(format("{:>10}", "test"), format("{:<10}", "test"), sep=" | "); 24 | // expect: " test | test " 25 | -------------------------------------------------------------------------------- /tests/integration/builtin_functions/int.ai: -------------------------------------------------------------------------------- 1 | print(int(1.5)); // expect: 1 2 | print(int(-1.5)); // expect: -1 3 | print(int(0)); // expect: 0 4 | print(int("123")); // expect: 123 5 | print(int("-456")); // expect: -456 6 | print(int(true)); // expect: 1 7 | print(int(false)); // expect: 0 8 | print(int(nil)); // expect: 0 -------------------------------------------------------------------------------- /tests/integration/builtin_functions/invalid_arg_count.ai: -------------------------------------------------------------------------------- 1 | map(); // expect runtime error: map() takes exactly 2 arguments. -------------------------------------------------------------------------------- /tests/integration/builtin_functions/map.ai: -------------------------------------------------------------------------------- 1 | let nums = [1, 2, 3]; 2 | print(map(nums, |x| x + 1)); // expect: [2, 3, 4] 3 | fn add_one(x) { 4 | return x + 1; 5 | } 6 | print(map(nums, add_one)); // expect: [2, 3, 4] 7 | 8 | let nums2 = [1, 2, 3, 4]; 9 | print(map(nums2, |x| x * 2)); // expect: [2, 4, 6, 8] 10 | let double = |x| x * 2; 11 | print(map(nums2, double)); // expect: [2, 4, 6, 8] 12 | print(map(nums2, |x| { 13 | // long body lambda 14 | print("x =", x); 15 | return x * 2; 16 | })); 17 | // expect: x = 1 18 | // expect: x = 2 19 | // expect: x = 3 20 | // expect: x = 4 21 | // expect: [2, 4, 6, 8] 22 | 23 | let words = ["hello", "world"]; 24 | print(map(words, |s| s + "!")); // expect: [hello!, world!] 25 | 26 | let empty = []; 27 | print(map(empty, |x| x + 1)); // expect: [] 28 | -------------------------------------------------------------------------------- /tests/integration/builtin_functions/print.ai: -------------------------------------------------------------------------------- 1 | // Basic usage 2 | print(1, 2, 3); // expect: 1 2 3 3 | 4 | // Custom separator 5 | print(1, 2, 3, sep=", "); // expect: 1, 2, 3 6 | 7 | // Custom ending 8 | print(1, 2, 3, end=""); 9 | 10 | // Multiple types 11 | print("Hello", 42, true, sep="--"); 12 | // expect: 1 2 3Hello--42--true 13 | 14 | // Custom separator and ending 15 | print(1, 2, 3, sep=", ", end="!"); 16 | 17 | // Force flush 18 | print("Processing...", end="", flush=true); 19 | // expect: 1, 2, 3!Processing... -------------------------------------------------------------------------------- /tests/integration/builtin_functions/str.ai: -------------------------------------------------------------------------------- 1 | print(str(123)); // expect: 123 2 | print(str(-456)); // expect: -456 3 | print(str(1.5)); // expect: 1.5 4 | print(str(true)); // expect: true 5 | print(str(false)); // expect: false 6 | print(str(nil)); // expect: nil 7 | print(str("hello")); // expect: hello 8 | let arr = [1, 2, 3]; 9 | print(str(arr)); // expect: [1, 2, 3] 10 | -------------------------------------------------------------------------------- /tests/integration/builtin_functions/zip.ai: -------------------------------------------------------------------------------- 1 | let a = [1, 2, 3]; 2 | let b = ["a", "b", "c"]; 3 | print(zip(a, b)); // expect: [[1, a], [2, b], [3, c]] 4 | 5 | let short = [1, 2]; 6 | let long = ["a", "b", "c", "d"]; 7 | print(zip(short, long)); // expect: [[1, a], [2, b]] 8 | print(zip(long, short)); // expect: [[a, 1], [b, 2]] 9 | 10 | let x = [1, 2, 3]; 11 | let y = ["a", "b", "c"]; 12 | let z = [true, false, true]; 13 | print(zip(x, y, z)); // expect: [[1, a, true], [2, b, false], [3, c, true]] 14 | 15 | let nums = [1, 2, 3]; 16 | let empty = []; 17 | print(zip(nums, empty)); // expect: [] 18 | print(zip(empty, nums)); // expect: [] 19 | 20 | let has_nil = [1, nil, 3]; 21 | let other = ["a", "b", "c"]; 22 | print(zip(has_nil, other)); // expect: [[1, a], [nil, b], [3, c]] -------------------------------------------------------------------------------- /tests/integration/call/bool.ai: -------------------------------------------------------------------------------- 1 | true(); // expect runtime error: Can only call functions and classes. 2 | -------------------------------------------------------------------------------- /tests/integration/call/nil.ai: -------------------------------------------------------------------------------- 1 | nil(); // expect runtime error: Can only call functions and classes. 2 | -------------------------------------------------------------------------------- /tests/integration/call/num.ai: -------------------------------------------------------------------------------- 1 | 123(); // expect runtime error: Can only call functions and classes. 2 | -------------------------------------------------------------------------------- /tests/integration/call/object.ai: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | let foo = Foo(); 4 | foo(); // expect runtime error: Can only call functions and classes. 5 | -------------------------------------------------------------------------------- /tests/integration/call/string.ai: -------------------------------------------------------------------------------- 1 | "str"(); // expect runtime error: Can only call functions and classes. 2 | -------------------------------------------------------------------------------- /tests/integration/class/empty.ai: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | print(Foo); // expect: Foo 4 | -------------------------------------------------------------------------------- /tests/integration/class/inherit_self.ai: -------------------------------------------------------------------------------- 1 | class Foo(Foo) {} // Error at 'Foo': A class can't inherit from itself. 2 | -------------------------------------------------------------------------------- /tests/integration/class/inherited_method.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn inFoo(self) { 3 | print("in foo"); 4 | } 5 | } 6 | 7 | class Bar(Foo) { 8 | fn inBar(self) { 9 | print("in bar"); 10 | } 11 | } 12 | 13 | class Baz(Bar) { 14 | fn inBaz(self) { 15 | print("in baz"); 16 | } 17 | } 18 | 19 | let baz = Baz(); 20 | baz.inFoo(); // expect: in foo 21 | baz.inBar(); // expect: in bar 22 | baz.inBaz(); // expect: in baz 23 | -------------------------------------------------------------------------------- /tests/integration/class/local_inherit_other.ai: -------------------------------------------------------------------------------- 1 | class A {} 2 | 3 | fn f() { 4 | class B(A) {} 5 | return B; 6 | } 7 | 8 | print(f()); // expect: B 9 | -------------------------------------------------------------------------------- /tests/integration/class/local_inherit_self.ai: -------------------------------------------------------------------------------- 1 | { 2 | class Foo(Foo) {} // Error at 'Foo': A class can't inherit from itself. 3 | } 4 | // [c line 5] Error at end: Expect '}' after block. 5 | -------------------------------------------------------------------------------- /tests/integration/class/local_reference_self.ai: -------------------------------------------------------------------------------- 1 | { 2 | class Foo { 3 | fn returnSelf(self) { 4 | return Foo; 5 | } 6 | } 7 | 8 | print(Foo().returnSelf()); // expect: Foo 9 | } 10 | -------------------------------------------------------------------------------- /tests/integration/class/methods.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | pub fn f0() {} 3 | 4 | ai fn f1() { 5 | prompt "1 + 1 =?"; 6 | } 7 | 8 | fn f2() { 9 | prompt "1 + 1 =?"; // Error at 'prompt': Can't prompt outside of ai function or root script. 10 | } 11 | 12 | f3() {} // Error at 'f3': Expect 'fn' or 'ai fn' modifier for method. 13 | } -------------------------------------------------------------------------------- /tests/integration/class/reference_self.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn returnSelf(self) { 3 | return Foo; 4 | } 5 | } 6 | 7 | print(Foo().returnSelf()); // expect: Foo 8 | -------------------------------------------------------------------------------- /tests/integration/class_fields/class_as_type_hint.ai: -------------------------------------------------------------------------------- 1 | class Point { 2 | x: int = 0, 3 | y: int = 0, 4 | } 5 | 6 | class Foo { 7 | point: Point, 8 | 9 | fn print_point(self) { 10 | print(self.point.x, self.point.y, sep=", "); 11 | } 12 | } 13 | 14 | Foo(Point()).print_point(); // expect: 0, 0 15 | Foo(point=Point(1, 1)).print_point(); // expect: 1, 1 16 | Foo(point=Point(1, y=1)).print_point(); // expect: 1, 1 17 | Foo(point=Point(x=1, y=1)).print_point(); // expect: 1, 1 18 | Foo(point=Point(y=1, x=1)).print_point(); // expect: 1, 1 19 | 20 | // ignore: not support yet 21 | class Bar { 22 | point: Point = Point(1, 1), 23 | 24 | fn print_point(self) { 25 | print(self.point.x, self.point.y, sep=", "); 26 | } 27 | } 28 | 29 | Foo(Point()).print_point(); // expect: 0, 0 30 | Foo(point=Point(2, 2)).print_point(); // expect: 1, 1 31 | Foo(point=Point(2, y=2)).print_point(); // expect: 1, 1 32 | Foo(point=Point(x=2, y=2)).print_point(); // expect: 1, 1 33 | Foo(point=Point(y=2, x=2)).print_point(); // expect: 1, 1 34 | -------------------------------------------------------------------------------- /tests/integration/class_fields/empty_constructor.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | a: int, 3 | b: int = 0, 4 | fn new() {} 5 | } 6 | let f = Foo(); 7 | print(f.a, f.b, sep=", "); // expect: nil, 0 8 | -------------------------------------------------------------------------------- /tests/integration/class_fields/explicit_constructor.ai: -------------------------------------------------------------------------------- 1 | // Only count self.field assignments 2 | class Point1 { 3 | x: int = 0, 4 | y: int, 5 | z: str = "zz", 6 | 7 | fn new(x: int, y: int) { 8 | let o = {a: 1}; 9 | o.a = 2; // NOT counted as a field initialization 10 | self.x = x; // Counted as initializing x 11 | self.y = y; // Counted as initializing y 12 | let v = 100; 13 | self.v = v; 14 | } 15 | } 16 | 17 | let p = Point1(10, 11); 18 | print(p.x, p.y, p.z, p.v, sep=", "); // expect: 10, 11, zz, 100 19 | 20 | // Allow non-declared fields 21 | class Point2 { 22 | x: int = 0, 23 | y: int, 24 | z: str = "zz", 25 | 26 | fn new(x: int, y: int, other: bool) { 27 | self.x = x; 28 | self.y = y; 29 | self.other = other; 30 | } 31 | } 32 | let p = Point2(5, 9, other=false); 33 | print(p.x, p.y, p.other, sep=", "); // expect: 5, 9, false 34 | -------------------------------------------------------------------------------- /tests/integration/class_fields/implicit_constructor.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | a: int, 3 | b: int = 0, 4 | c: str = "cc", 5 | d: bool = true, 6 | } 7 | 8 | let f = Foo(1); 9 | print(f.a, f.b, f.c, f.d, sep=", "); // expect: 1, 0, cc, true 10 | 11 | class Point { 12 | x: int = 0, 13 | y: int = 0, 14 | } 15 | let p1 = Point(); 16 | print(p1.x, p1.y); // expect: 0 0 17 | let p2 = Point(x=1); 18 | print(p2.x, p2.y); // expect: 1 0 19 | let p3 = Point(y=1); 20 | print(p3.x, p3.y); // expect: 0 1 21 | let p4 = Point(x=1, y=1); 22 | print(p4.x, p4.y); // expect: 1 1 23 | -------------------------------------------------------------------------------- /tests/integration/class_fields/syntax_error.ai: -------------------------------------------------------------------------------- 1 | class A { 2 | a 3 | } // Error at '}': Expect ':' after field name. 4 | 5 | class Foo { 6 | a, int // Error at ',': Expect ':' after field name. 7 | } 8 | 9 | class Bar { 10 | a: 1 // Error at '1': Invalid type annotation. 11 | } 12 | 13 | class Baz { 14 | a: int; // Error at ';': Expect ',' after field declaration. 15 | } // Error at '}': Expect expression. 16 | 17 | class Faz { 18 | a: int =, // Error at ',': Expect default value after '='. 19 | } 20 | 21 | class Fab { 22 | a: int = 23 | } // Error at '}': Expect default value after '='. 24 | 25 | class Fax { 26 | a: int = 1 27 | } // Error at '}': Expect ',' after field declaration. 28 | 29 | class Fbx { 30 | a: int = value, // Error at 'value': Only allow set literal (number, string, bool) as the default value. 31 | } -------------------------------------------------------------------------------- /tests/integration/class_literal/initialize.ai: -------------------------------------------------------------------------------- 1 | class Person { 2 | name: str, 3 | age: int = 18, 4 | } 5 | 6 | let p = Person { name: "Alice" }; 7 | print(p.name, p.age); // expect: Alice 18 8 | 9 | let p = Person { name: "Alice", age: 20 }; 10 | print(p.name, p.age); // expect: Alice 20 11 | 12 | let name = "Alice"; 13 | let age = 30; 14 | let p = Person { name, age }; 15 | print(p.name, p.age); // expect: Alice 30 16 | 17 | let name = "Alice"; 18 | let p = Person { name, age: 40 }; 19 | -------------------------------------------------------------------------------- /tests/integration/closure/assign_to_closure.ai: -------------------------------------------------------------------------------- 1 | let f; 2 | let g; 3 | 4 | { 5 | let local = "local"; 6 | fn f_() { 7 | print(local); 8 | local = "after f"; 9 | print(local); 10 | } 11 | f = f_; 12 | 13 | fn g_() { 14 | print(local); 15 | local = "after g"; 16 | print(local); 17 | } 18 | g = g_; 19 | } 20 | 21 | f(); 22 | // expect: local 23 | // expect: after f 24 | 25 | g(); 26 | // expect: after f 27 | // expect: after g 28 | -------------------------------------------------------------------------------- /tests/integration/closure/assign_to_shadowed_later.ai: -------------------------------------------------------------------------------- 1 | let a = "global"; 2 | 3 | { 4 | fn assign() { 5 | a = "assigned"; 6 | } 7 | 8 | let a = "inner"; 9 | assign(); 10 | print(a); // expect: inner 11 | } 12 | 13 | print(a); // expect: assigned 14 | -------------------------------------------------------------------------------- /tests/integration/closure/close_over_function_parameter.ai: -------------------------------------------------------------------------------- 1 | let f; 2 | 3 | fn foo(param) { 4 | fn f_() { 5 | print(param); 6 | } 7 | f = f_; 8 | } 9 | foo("param"); 10 | 11 | f(); // expect: param 12 | -------------------------------------------------------------------------------- /tests/integration/closure/close_over_later_variable.ai: -------------------------------------------------------------------------------- 1 | // This is a regression test. There was a bug where if an upvalue for an 2 | // earlier local (here "a") was captured *after* a later one ("b"), then it 3 | // would crash because it walked to the end of the upvalue list (correct), but 4 | // then didn't handle not finding the variable. 5 | 6 | fn f() { 7 | let a = "a"; 8 | let b = "b"; 9 | fn g() { 10 | print(b); // expect: b 11 | print(a); // expect: a 12 | } 13 | g(); 14 | } 15 | f(); 16 | -------------------------------------------------------------------------------- /tests/integration/closure/close_over_method_parameter.ai: -------------------------------------------------------------------------------- 1 | let f; 2 | 3 | class Foo { 4 | fn method(self, param) { 5 | fn f_() { 6 | print(param); 7 | } 8 | f = f_; 9 | } 10 | } 11 | 12 | Foo().method("param"); 13 | f(); // expect: param 14 | -------------------------------------------------------------------------------- /tests/integration/closure/closed_closure_in_function.ai: -------------------------------------------------------------------------------- 1 | let f; 2 | 3 | { 4 | let local = "local"; 5 | fn f_() { 6 | print(local); 7 | } 8 | f = f_; 9 | } 10 | 11 | f(); // expect: local 12 | -------------------------------------------------------------------------------- /tests/integration/closure/nested_closure.ai: -------------------------------------------------------------------------------- 1 | let f; 2 | 3 | fn f1() { 4 | let a = "a"; 5 | fn f2() { 6 | let b = "b"; 7 | fn f3() { 8 | let c = "c"; 9 | fn f4() { 10 | print(a); 11 | print(b); 12 | print(c); 13 | } 14 | f = f4; 15 | } 16 | f3(); 17 | } 18 | f2(); 19 | } 20 | f1(); 21 | 22 | f(); 23 | // expect: a 24 | // expect: b 25 | // expect: c 26 | -------------------------------------------------------------------------------- /tests/integration/closure/open_closure_in_function.ai: -------------------------------------------------------------------------------- 1 | { 2 | let local = "local"; 3 | fn f() { 4 | print(local); // expect: local 5 | } 6 | f(); 7 | } 8 | -------------------------------------------------------------------------------- /tests/integration/closure/reference_closure_multiple_times.ai: -------------------------------------------------------------------------------- 1 | let f; 2 | 3 | { 4 | let a = "a"; 5 | fn f_() { 6 | print(a); 7 | print(a); 8 | } 9 | f = f_; 10 | } 11 | 12 | f(); 13 | // expect: a 14 | // expect: a 15 | -------------------------------------------------------------------------------- /tests/integration/closure/reuse_closure_slot.ai: -------------------------------------------------------------------------------- 1 | { 2 | let f; 3 | 4 | { 5 | let a = "a"; 6 | fn f_() { print(a); } 7 | f = f_; 8 | } 9 | 10 | { 11 | // Since a is out of scope, the local slot will be reused by b. Make sure 12 | // that f still closes over a. 13 | let b = "b"; 14 | f(); // expect: a 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /tests/integration/closure/shadow_closure_with_local.ai: -------------------------------------------------------------------------------- 1 | { 2 | let foo = "closure"; 3 | fn f() { 4 | { 5 | print(foo); // expect: closure 6 | let foo = "shadow"; 7 | print(foo); // expect: shadow 8 | } 9 | print(foo); // expect: closure 10 | } 11 | f(); 12 | } 13 | -------------------------------------------------------------------------------- /tests/integration/closure/unused_closure.ai: -------------------------------------------------------------------------------- 1 | // This is a regression test. There was a bug where the VM would try to close 2 | // an upvalue even if the upvalue was never created because the codepath for 3 | // the closure was not executed. 4 | 5 | { 6 | let a = "a"; 7 | if false { 8 | fn foo() { a; } 9 | } 10 | } 11 | 12 | // If we get here, we didn't segfault when a went out of scope. 13 | print("ok"); // expect: ok 14 | -------------------------------------------------------------------------------- /tests/integration/closure/unused_later_closure.ai: -------------------------------------------------------------------------------- 1 | // This is a regression test. When closing upvalues for discarded locals, it 2 | // wouldn't make sure it discarded the upvalue for the correct stack slot. 3 | // 4 | // Here we create two locals that can be closed over, but only the first one 5 | // actually is. When "b" goes out of scope, we need to make sure we don't 6 | // prematurely close "a". 7 | let closure; 8 | 9 | { 10 | let a = "a"; 11 | 12 | { 13 | let b = "b"; 14 | fn returnA() { 15 | return a; 16 | } 17 | 18 | closure = returnA; 19 | 20 | if false { 21 | fn returnB() { 22 | return b; 23 | } 24 | } 25 | } 26 | 27 | print(closure()); // expect: a 28 | } 29 | -------------------------------------------------------------------------------- /tests/integration/comments/line_at_eof.ai: -------------------------------------------------------------------------------- 1 | print("ok"); // expect: ok 2 | // comment -------------------------------------------------------------------------------- /tests/integration/comments/only_line_comment.ai: -------------------------------------------------------------------------------- 1 | // comment -------------------------------------------------------------------------------- /tests/integration/comments/only_line_comment_and_line.ai: -------------------------------------------------------------------------------- 1 | // comment 2 | -------------------------------------------------------------------------------- /tests/integration/comments/unicode.ai: -------------------------------------------------------------------------------- 1 | // Unicode characters are allowed in comments. 2 | // 3 | // Latin 1 Supplement: £§¶ÜÞ 4 | // Latin Extended-A: ĐĦŋœ 5 | // Latin Extended-B: ƂƢƩǁ 6 | // Other stuff: ឃᢆ᯽₪ℜ↩⊗┺░ 7 | // Emoji: ☃☺♣ 8 | 9 | print("ok"); // expect: ok 10 | -------------------------------------------------------------------------------- /tests/integration/compound_assign/add.ai: -------------------------------------------------------------------------------- 1 | let i = 0; 2 | i += 1; 3 | print(i); // expect: 1 4 | 5 | i += -1; 6 | print(i); // expect: 0 7 | -------------------------------------------------------------------------------- /tests/integration/compound_assign/add_nonnum_num.ai: -------------------------------------------------------------------------------- 1 | let i = 1; 2 | i += "1"; // expect runtime error: Operands must be two numbers or two strings. 3 | -------------------------------------------------------------------------------- /tests/integration/compound_assign/add_num_nonnum.ai: -------------------------------------------------------------------------------- 1 | let i = 1; 2 | "1" += i; // Error at '+=': Invalid assignment target. 3 | -------------------------------------------------------------------------------- /tests/integration/compound_assign/all.ai: -------------------------------------------------------------------------------- 1 | let i = 1; 2 | i *= 2; 3 | print(i); // expect: 2 4 | i += 3; 5 | print(i); // expect: 5 6 | i /= 2; 7 | print(i); // expect: 2.5 8 | i -= 1.5; 9 | print(i); // expect: 1 10 | i %= 2; 11 | print(i); // expect: 1 -------------------------------------------------------------------------------- /tests/integration/compound_assign/divide.ai: -------------------------------------------------------------------------------- 1 | let i = 2; 2 | i /= 2; 3 | print(i); // expect: 1 4 | 5 | i /= -1; 6 | print(i); // expect: -1 -------------------------------------------------------------------------------- /tests/integration/compound_assign/divide_nonnum_num.ai: -------------------------------------------------------------------------------- 1 | let i = 1; 2 | i /= "1"; // expect runtime error: Operands must be numbers. 3 | -------------------------------------------------------------------------------- /tests/integration/compound_assign/divide_num_nonnum.ai: -------------------------------------------------------------------------------- 1 | let i = 1; 2 | "1" /= i; // Error at '/=': Invalid assignment target. 3 | -------------------------------------------------------------------------------- /tests/integration/compound_assign/minus.ai: -------------------------------------------------------------------------------- 1 | let i = 1; 2 | i -= 1; 3 | print(i); // expect: 0 4 | 5 | i -= -1; 6 | print(i); // expect: 1 -------------------------------------------------------------------------------- /tests/integration/compound_assign/minus_nonnum_num.ai: -------------------------------------------------------------------------------- 1 | let i = 1; 2 | i /= "1"; // expect runtime error: Operands must be numbers. 3 | -------------------------------------------------------------------------------- /tests/integration/compound_assign/minus_num_nonnum.ai: -------------------------------------------------------------------------------- 1 | let i = 1; 2 | "1" -= i; // Error at '-=': Invalid assignment target. 3 | -------------------------------------------------------------------------------- /tests/integration/compound_assign/modulo.ai: -------------------------------------------------------------------------------- 1 | let i = 2; 2 | i %= 2; 3 | print(i); // expect: 0 4 | -------------------------------------------------------------------------------- /tests/integration/compound_assign/modulo_nonnum_num.ai: -------------------------------------------------------------------------------- 1 | let i = 1; 2 | i -= "1"; // expect runtime error: Operands must be numbers. 3 | -------------------------------------------------------------------------------- /tests/integration/compound_assign/modulo_num_nonnum.ai: -------------------------------------------------------------------------------- 1 | let i = 1; 2 | "1" %= i; // Error at '%=': Invalid assignment target. 3 | -------------------------------------------------------------------------------- /tests/integration/compound_assign/multiply.ai: -------------------------------------------------------------------------------- 1 | let i = 1; 2 | i *= 2; 3 | print(i); // expect: 2 4 | 5 | i *= -1; 6 | print(i); // expect: -2 -------------------------------------------------------------------------------- /tests/integration/compound_assign/multiply_nonnum_num.ai: -------------------------------------------------------------------------------- 1 | let i = 1; 2 | i *= "1"; // expect runtime error: Operands must be numbers. 3 | -------------------------------------------------------------------------------- /tests/integration/compound_assign/multiply_num_nonnum.ai: -------------------------------------------------------------------------------- 1 | let i = 1; 2 | "1" *= i; // Error at '*=': Invalid assignment target. 3 | -------------------------------------------------------------------------------- /tests/integration/compound_assign/str.ai: -------------------------------------------------------------------------------- 1 | let i = "aa"; 2 | i += "bb"; 3 | print(i); // expect: aabb -------------------------------------------------------------------------------- /tests/integration/const/globals.ai: -------------------------------------------------------------------------------- 1 | const PI = 3.14; 2 | PI = 3.2; // Error at 'PI': Cannot assign to constant variable. 3 | print(PI); -------------------------------------------------------------------------------- /tests/integration/const/initializer.ai: -------------------------------------------------------------------------------- 1 | const PI; // Error at ';': Const declarations must have an initializer. -------------------------------------------------------------------------------- /tests/integration/const/locals.ai: -------------------------------------------------------------------------------- 1 | fn f1() { 2 | const PI = 3.14; 3 | PI = 3.1; // Error at 'PI': Cannot assign to constant variable. 4 | } 5 | f1(); -------------------------------------------------------------------------------- /tests/integration/const/shadow.ai: -------------------------------------------------------------------------------- 1 | const PI = 3.14; 2 | fn f1() { 3 | let PI = 3.1415; 4 | PI = 3.14159; 5 | print(PI); // expect: 3.14159 6 | } 7 | f1(); -------------------------------------------------------------------------------- /tests/integration/const/upvalues.ai: -------------------------------------------------------------------------------- 1 | fn f1() { 2 | const PI = 3.14; 3 | 4 | fn f2 () { 5 | PI = 3.2; // Error at 'PI': Cannot assign to constant variable. 6 | print(PI); 7 | } 8 | 9 | return f2; 10 | } 11 | let f = f1(); 12 | f(); -------------------------------------------------------------------------------- /tests/integration/constructor/arguments.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn new(a, b) { 3 | print("new"); // expect: new 4 | self.a = a; 5 | self.b = b; 6 | } 7 | } 8 | 9 | let foo = Foo(1, 2); 10 | print(foo.a); // expect: 1 11 | print(foo.b); // expect: 2 12 | -------------------------------------------------------------------------------- /tests/integration/constructor/call_init_early_return.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn new() { 3 | print("new"); 4 | return; 5 | print("nope"); 6 | } 7 | } 8 | 9 | let foo = Foo(); // expect: new 10 | print(foo.new()); // expect: new 11 | // expect: Foo {} 12 | -------------------------------------------------------------------------------- /tests/integration/constructor/call_init_explicitly.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn new(arg) { 3 | print("Foo.new(" + arg + ")"); 4 | self.field = "new"; 5 | } 6 | } 7 | 8 | let foo = Foo("one"); // expect: Foo.new(one) 9 | foo.field = "field"; 10 | 11 | let foo2 = foo.new("two"); // expect: Foo.new(two) 12 | print(foo2); // expect: Foo {field: new} 13 | 14 | // Make sure new() doesn't create a fresh instance. 15 | print(foo.field); // expect: new 16 | -------------------------------------------------------------------------------- /tests/integration/constructor/default.ai: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | let foo = Foo(); 4 | print(foo); // expect: Foo {} 5 | -------------------------------------------------------------------------------- /tests/integration/constructor/default_arguments.ai: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | let foo = Foo(1, 2, 3); // expect runtime error: Expected 0 arguments but got 3. 4 | -------------------------------------------------------------------------------- /tests/integration/constructor/early_return.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn new() { 3 | print("new"); 4 | return; 5 | print("nope"); 6 | } 7 | } 8 | 9 | let foo = Foo(); // expect: new 10 | print(foo); // expect: Foo {} 11 | -------------------------------------------------------------------------------- /tests/integration/constructor/extra_arguments.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn new(a, b) { 3 | self.a = a; 4 | self.b = b; 5 | } 6 | } 7 | 8 | let foo = Foo(1, 2, 3, 4); // expect runtime error: Expected 2 arguments but got 4. -------------------------------------------------------------------------------- /tests/integration/constructor/init_not_method.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn new(arg) { 3 | print("Foo.new(" + arg + ")"); 4 | self.field = "new"; 5 | } 6 | } 7 | 8 | fn new() { 9 | print("not constructor"); 10 | } 11 | 12 | new(); // expect: not constructor 13 | -------------------------------------------------------------------------------- /tests/integration/constructor/kwargs.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn new(a, b: str = "bb") { 3 | self.a = a; 4 | self.b = b; 5 | print(a, b, sep=", "); 6 | } 7 | } 8 | 9 | Foo(a="aa"); // expect: aa, bb 10 | Foo("aa", b="bbb"); // expect: aa, bbb 11 | Foo(a="aa", b="bbb"); // expect: aa, bbb 12 | Foo(b="bbb", a="aa"); // expect: aa, bbb 13 | -------------------------------------------------------------------------------- /tests/integration/constructor/missing_arguments.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn new(a, b) {} 3 | } 4 | 5 | let foo = Foo(1); // expect runtime error: Expected 2 arguments but got 1. 6 | -------------------------------------------------------------------------------- /tests/integration/constructor/return_in_nested_function.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn new() { 3 | fn new() { 4 | return "bar"; 5 | } 6 | print(new()); // expect: bar 7 | } 8 | } 9 | 10 | print(Foo()); // expect: Foo {} 11 | -------------------------------------------------------------------------------- /tests/integration/constructor/return_value.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn new() { 3 | return "result"; // Error at 'return': Can't return a value from an constructor. 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/integration/default_args/duplicate_keyword_args.ai: -------------------------------------------------------------------------------- 1 | fn test(a, b = "b", c: str = "c") { 2 | print(a); 3 | print(b + c); 4 | } 5 | 6 | test("a", a="bb", b="bb"); // expect runtime error: Keyword argument 'a' was already specified as positional argument. 7 | -------------------------------------------------------------------------------- /tests/integration/default_args/enum_variant_default_value.ai: -------------------------------------------------------------------------------- 1 | enum Either { 2 | A = 1, 3 | B = 2, 4 | } 5 | 6 | fn choose(a = Either::A, b: Either = Either::B ) { 7 | print(a, b); 8 | } 9 | 10 | choose(); // expect: Either::A(1) Either::B(2) 11 | choose(Either::B); // expect: Either::B(2) Either::B(2) 12 | choose(Either::B, Either::A); // expect: Either::B(2) Either::A(1) 13 | choose(Either::B, b=Either::A); // expect: Either::B(2) Either::A(1) 14 | choose(a=Either::B, b=Either::A); // expect: Either::B(2) Either::A(1) 15 | choose(b=Either::B, a=Either::A); // expect: Either::A(1) Either::B(2) 16 | -------------------------------------------------------------------------------- /tests/integration/default_args/invalid_default_args_declare_order.ai: -------------------------------------------------------------------------------- 1 | fn test1(b = "b", a, c: str = "c") { // Error at 'a': Positional parameter must come before parameter with a default. 2 | print(a + b + c); 3 | } 4 | fn test2(a, b = "b", bb, c: str = "c") { // Error at 'bb': Positional parameter must come before parameter with a default. 5 | print(a + b + c); 6 | } 7 | fn test3(a, b = "b", bb, c) { // Error at 'bb': Positional parameter must come before parameter with a default. 8 | print(a + b + c); 9 | } 10 | -------------------------------------------------------------------------------- /tests/integration/default_args/invalid_enum_arg_type.ai: -------------------------------------------------------------------------------- 1 | fn f(a = Eitherx::A) {} // Error at 'Eitherx': Invalid enum 'Eitherx'. 2 | -------------------------------------------------------------------------------- /tests/integration/default_args/invalid_enum_variant_arg_type.ai: -------------------------------------------------------------------------------- 1 | enum Either { A, B } 2 | fn f(a = Either::C) {} // Error at 'C': No variant called 'C' in enum 'Either'. -------------------------------------------------------------------------------- /tests/integration/default_args/invalid_keword_arg.ai: -------------------------------------------------------------------------------- 1 | fn test(a, b = "b", c: str = "c") { 2 | print(a); 3 | print(b + c); 4 | } 5 | 6 | test("a", bb="bb"); // expect runtime error: Unknown keyword argument 'bb'. 7 | -------------------------------------------------------------------------------- /tests/integration/default_args/missing_required_arg.ai: -------------------------------------------------------------------------------- 1 | fn test(a, b = "b", c: str = "c") { 2 | print(a); 3 | print(b + c); 4 | } 5 | 6 | test(); // expect runtime error: Missing required argument 'a'. 7 | -------------------------------------------------------------------------------- /tests/integration/default_args/normal.ai: -------------------------------------------------------------------------------- 1 | fn test(a, b = "b", c: str = "c") { 2 | print(a + b + c); 3 | } 4 | 5 | test("a"); // expect: abc 6 | test("a", "b"); // expect: abc 7 | test("a", "b", "c"); // expect: abc 8 | test("a", "bb"); // expect: abbc 9 | test("a", b="bb"); // expect: abbc 10 | test(a="a", c="cc"); // expect: abcc 11 | test("a", b="bb", c="cc"); // expect: abbcc 12 | test("a", c="cc"); // expect: abcc 13 | test(a="aa", b="bb", c="cc"); // expect: aabbcc 14 | test(b="bb", a="aa", c="cc"); // expect: aabbcc 15 | test(b="bb", c="cc", a="aa"); // expect: aabbcc 16 | test(c="cc", b="bb", a="aa"); // expect: aabbcc 17 | test(c="cc", a="aa", b="bb"); // expect: aabbcc 18 | test(c="cc", a="aa", b="bb"); // expect: aabbcc 19 | -------------------------------------------------------------------------------- /tests/integration/default_args/positional_arg_position_invalid.ai: -------------------------------------------------------------------------------- 1 | fn test(a, b = "b", c: str = "c") { 2 | print(a + b + c); 3 | } 4 | 5 | test(b="bb", "a"); // Error at ',': Positional arguments must come before keyword arguments. 6 | test("a", b="bb", "cc"); // Error at ',': Positional arguments must come before keyword arguments. -------------------------------------------------------------------------------- /tests/integration/directive_validators/syntax.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | @string(max_len=10) 3 | @string(min_len=10) 4 | name: str, 5 | age: int = 18, 6 | } -------------------------------------------------------------------------------- /tests/integration/empty_file.ai: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/aiscriptdev/aiscript/d0aab785ff719238e37274dc200658a8b4b596af/tests/integration/empty_file.ai -------------------------------------------------------------------------------- /tests/integration/enum/enum_double_colon.ai: -------------------------------------------------------------------------------- 1 | enum Result { Ok, Error } 2 | 3 | 1::Ok; // Error at '::': Expected enum name before '::'. 4 | Result::; // Error at ';': Expect variant name after '::'. 5 | Result:::; // Error at ':': Expect variant name after '::'. 6 | Result:::Ok; // Error at ':': Expect variant name after '::'. 7 | -------------------------------------------------------------------------------- /tests/integration/enum/enum_invalid_method1.ai: -------------------------------------------------------------------------------- 1 | enum Either { 2 | A = 1, 3 | B = 2, 4 | } 5 | 6 | Either::A.invalid(); // expect runtime error: Undefined method 'invalid' of enum 'Either'. -------------------------------------------------------------------------------- /tests/integration/enum/enum_invalid_method2.ai: -------------------------------------------------------------------------------- 1 | enum Either { 2 | A = 1, 3 | B = 2, 4 | } 5 | 6 | let e = Either::A; 7 | e.invalid(); // expect runtime error: Undefined method 'invalid' of enum 'Either'. -------------------------------------------------------------------------------- /tests/integration/enum/enum_invalid_method3.ai: -------------------------------------------------------------------------------- 1 | enum Either { 2 | A = 1, 3 | B = 2, 4 | 5 | fn f(self) { 6 | print("f"); 7 | } 8 | } 9 | 10 | Either.f(); // expect runtime error: Undefined static method 'f' of enum 'Either'. -------------------------------------------------------------------------------- /tests/integration/enum/enum_method.ai: -------------------------------------------------------------------------------- 1 | enum Either { 2 | A = 1, 3 | 4 | fn f1(self) { 5 | print(self); 6 | } 7 | 8 | fn f2(self) { 9 | print([self]); 10 | } 11 | 12 | fn f3() { 13 | print("static method"); 14 | } 15 | } 16 | 17 | Either::A.f1(); // expect: Either::A(1) 18 | let e = Either::A; 19 | e.f1(); // expect: Either::A(1) 20 | e.f2(); // expect: 1 21 | Either.f3(); // expect: static method 22 | -------------------------------------------------------------------------------- /tests/integration/enum/enum_variant_assign.ai: -------------------------------------------------------------------------------- 1 | enum Either { 2 | A, 3 | B, 4 | } 5 | 6 | let x = Either::A; 7 | print(x); 8 | x = Either::B; 9 | print(x); 10 | 11 | Either::A = 1; // Error at '=': Invalid assignment target. 12 | Either::A = Either::B; // Error at '=': Invalid assignment target. -------------------------------------------------------------------------------- /tests/integration/enum/expect_name.ai: -------------------------------------------------------------------------------- 1 | enum {} // Error at '{': Expect enum name. 2 | 3 | enum MissingVariant1 { 4 | "abc" // Error at 'abc': Expect variant name. 5 | } 6 | 7 | enum MissingVariant2 { 8 | 1 // Error at '1': Expect variant name. 9 | } 10 | 11 | enum MissingVariant3 { 12 | true // Error at 'true': Expect variant name. 13 | } 14 | 15 | enum MissingVariant4 { 16 | , // Error at ',': Expect variant name. 17 | } 18 | -------------------------------------------------------------------------------- /tests/integration/enum/integer_value.ai: -------------------------------------------------------------------------------- 1 | enum Number1 { 2 | One, 3 | Two, 4 | Three = 3, 5 | } 6 | 7 | print(Number1.One); // expect: nil 8 | print(Number1.Two); // expect: nil 9 | print(Number1.Three); // expect: 3 10 | 11 | enum Number2 { 12 | One = 1, 13 | Two, 14 | } 15 | 16 | print(Number2.One); // expect: 1 17 | print(Number2.Two); // expect: 2 18 | 19 | enum Number3 { 20 | One = 1, 21 | Two = 3, 22 | } 23 | 24 | print(Number3.One); // expect: 1 25 | print(Number3.Two); // expect: 3 26 | 27 | enum E1 { 28 | A = 1, 29 | B, 30 | C = 10, 31 | D, 32 | } 33 | 34 | print(E1.A); // expect: 1 35 | print(E1.B); // expect: 2 36 | print(E1.C); // expect: 10 37 | print(E1.D); // expect: 11 38 | -------------------------------------------------------------------------------- /tests/integration/enum/invalid_enum.ai: -------------------------------------------------------------------------------- 1 | class A {} 2 | 3 | let a = A::Pending; // Error at 'A': Invalid enum 'A'. 4 | -------------------------------------------------------------------------------- /tests/integration/enum/invalid_enum_inherit.ai: -------------------------------------------------------------------------------- 1 | enum Base {} 2 | enum Either(Base) { // Error at '(': Enum doesn't support inherit. 3 | A = 1, 4 | B, 5 | } 6 | 7 | enum A() { // Error at '(': Expect '{' before enum body. 8 | } -------------------------------------------------------------------------------- /tests/integration/enum/invalid_enum_value_type.ai: -------------------------------------------------------------------------------- 1 | enum Invalid { 2 | A = +, // Error at '+': Enum variant value must be a literal (number, string, or boolean) 3 | } 4 | 5 | let a = 1; 6 | enum Invalid1 { 7 | A = a, // Error at 'a': Enum variant value must be a literal (number, string, or boolean) 8 | } 9 | 10 | enum Either { A, B } 11 | enum Invalid2 { 12 | A = Either::A, // Error at 'Either': Enum variant value must be a literal (number, string, or boolean) 13 | } 14 | -------------------------------------------------------------------------------- /tests/integration/enum/invalid_enum_variant.ai: -------------------------------------------------------------------------------- 1 | class A {} 2 | 3 | let a = A.Pending; // expect runtime error: Only instances have properties. -------------------------------------------------------------------------------- /tests/integration/enum/invalid_variant.ai: -------------------------------------------------------------------------------- 1 | enum Empty {} 2 | 3 | print(Empty::InvalidVariant); // Error at 'InvalidVariant': No variant called 'InvalidVariant' in enum 'Empty'. -------------------------------------------------------------------------------- /tests/integration/enum/invalid_variant_access_value.ai: -------------------------------------------------------------------------------- 1 | enum Empty {} 2 | 3 | print(Empty.InvalidVariant); // expect runtime error: Undefined property 'InvalidVariant' -------------------------------------------------------------------------------- /tests/integration/enum/missing_punct.ai: -------------------------------------------------------------------------------- 1 | enum Either { 2 | A: 1 // Error at ':': Expect ',' after variant. 3 | } 4 | 5 | enum Either1 // { 6 | A, // Error at 'A': Expect '{' before enum body. 7 | B, 8 | } 9 | 10 | enum Either2 { 11 | A 12 | B, // Error at 'B': Expect ',' after variant. 13 | } 14 | 15 | enum Either3 { 16 | A, 17 | B, 18 | // Error at end: Expect '}' after enum body. -------------------------------------------------------------------------------- /tests/integration/enum/syntax.ai: -------------------------------------------------------------------------------- 1 | enum Empty {} // Empty enum 2 | 3 | enum HttpStatus { 4 | Ok = 200, 5 | NotFound = 404, 6 | ServerError = 500, 7 | 8 | pub ai fn as_str(self) { 9 | print("as_str"); 10 | } 11 | pub fn as_str2(self) { 12 | print("as_str2"); 13 | } 14 | ai fn as_str3(self) { 15 | print("as_str3"); 16 | } 17 | fn as_str4(self) { 18 | print("as_str4"); 19 | } 20 | } 21 | 22 | let status = HttpStatus::Ok; 23 | print(status); // expect: HttpStatus::Ok(200) 24 | let status = HttpStatus.Ok; 25 | print(status); // expect: 200 26 | 27 | print(HttpStatus::NotFound); // expect: HttpStatus::NotFound(404) 28 | print(HttpStatus.NotFound); // expect: 404 29 | 30 | let status = HttpStatus::ServerError; 31 | status.as_str(); // expect: as_str 32 | HttpStatus::Ok.as_str2(); // expect: as_str2 33 | HttpStatus::NotFound.as_str3(); // expect: as_str3 34 | HttpStatus::ServerError.as_str4(); // expect: as_str4 35 | -------------------------------------------------------------------------------- /tests/integration/enum/variant_type_check.ai: -------------------------------------------------------------------------------- 1 | enum Invalid { 2 | A = "string", 3 | B = true // Error at 'B': Enum variant 'B' must be of type string 4 | } 5 | 6 | enum Invalid2 { 7 | A = "text", 8 | B // Error at 'B': Must specify value for non-integer enum variants 9 | } 10 | 11 | enum Invalid3 { 12 | A = 5, 13 | B = 3, // Error at 'B': Enum variant 'B' value 3 must be greater than or equal to 6 (next auto-increment value) 14 | } 15 | 16 | enum Invalid4 { 17 | A = 3, 18 | B = 3, // Error at 'B': Enum variant 'B' value 3 must be greater than or equal to 4 (next auto-increment value) 19 | } 20 | 21 | enum Invalid5 { 22 | A = "A", 23 | B = "A", // Error at 'B': Duplicate value "A" in enum variant 'B'. 24 | } 25 | 26 | enum Invalid6 { 27 | A, 28 | A = "A", // Error at 'A': Duplicate variant 'A' in enum 'Invalid6'. 29 | } 30 | 31 | enum Status { 32 | Pending = 0, 33 | Active = 1, 34 | Failed = false, // Error at 'Failed': Enum variant 'Failed' must be of type integer 35 | } 36 | 37 | enum Config { 38 | Debug = true, 39 | Log = false, 40 | Mode = 1, // Error at 'Mode': Enum variant 'Mode' must be of type boolean 41 | } 42 | 43 | enum FileType { 44 | Text = "text/plain", 45 | Html = "text/html", 46 | Json = true, // Error at 'Json': Enum variant 'Json' must be of type string 47 | } 48 | -------------------------------------------------------------------------------- /tests/integration/enum_evaluate/array.ai: -------------------------------------------------------------------------------- 1 | enum Foo { A = "a", B = "b" } 2 | 3 | print([[Foo::A], [Foo::B]]); // expect: [a, b] 4 | 5 | let a = [[Foo::A], [Foo::B]]; 6 | print(a); // expect: [a, b] 7 | 8 | print([[Foo.A], [Foo.B]]); // expect: [[a], [b]] 9 | let a = [[Foo.A], [Foo.B]]; 10 | print(a); // expect: [[a], [b]] 11 | -------------------------------------------------------------------------------- /tests/integration/enum_evaluate/not_enum_variant1.ai: -------------------------------------------------------------------------------- 1 | let x = 1; 2 | [x]; // expect runtime error: The variable is not an enum variant and is not evaluable. To declare a single-element array, use [element,] syntax instead of [element]. 3 | -------------------------------------------------------------------------------- /tests/integration/enum_evaluate/not_enum_variant2.ai: -------------------------------------------------------------------------------- 1 | let x = "x"; 2 | [x]; // expect runtime error: The variable is not an enum variant and is not evaluable. To declare a single-element array, use [element,] syntax instead of [element]. 3 | -------------------------------------------------------------------------------- /tests/integration/enum_evaluate/not_enum_variant3.ai: -------------------------------------------------------------------------------- 1 | enum E { A = "a" } 2 | let x = E.A; 3 | [x]; // expect runtime error: The variable is not an enum variant and is not evaluable. To declare a single-element array, use [element,] syntax instead of [element]. 4 | -------------------------------------------------------------------------------- /tests/integration/enum_evaluate/syntax.ai: -------------------------------------------------------------------------------- 1 | enum Either { A, B } 2 | 3 | print([Either::A]); // expect: nil 4 | print([Either::A,]); // expect: [Either::A] 5 | print([(Either::A)]); // expect: [Either::A] 6 | 7 | let b = Either::B; 8 | print([b]); // expect: nil 9 | print([(b)]); // expect: [Either::B] 10 | print([b,]); // expect: [Either::B] 11 | 12 | enum Num { 13 | One = 1, 14 | Two = 2, 15 | } 16 | print([Num::One]); // expect: 1 17 | print([Num::Two]); // expect: 2 18 | print([Num::Two] + 1); // expect: 3 19 | print([Num::One,]); // expect: [Num::One(1)] 20 | print([(Num::One)]); // expect: [Num::One(1)] 21 | 22 | enum Str { 23 | A = "a", 24 | B = "b", 25 | } 26 | print([Str::A]); // expect: "a" 27 | print([Str::B]); // expect: "b" 28 | 29 | let x = Str::A; 30 | print([x]); // expect: "a" 31 | print([x] + "b"); // expect: "ab" 32 | print([(x)]); // expect: [Str::A(a)] 33 | print([x,]); // expect: [Str::A(a)] 34 | -------------------------------------------------------------------------------- /tests/integration/env/normal.ai: -------------------------------------------------------------------------------- 1 | let home = $HOME; 2 | print(home.is_empty()); // expect: false 3 | let h = "HOME"; 4 | print($h.is_empty()); // expect: true 5 | print($(h).is_empty()); // expect: false 6 | print($HOME == $(h)); // expect: true 7 | print($INVALID.is_empty()); // expect: true 8 | print($INVALID == ""); // expect: true 9 | print($("HOME").is_empty()); // expect: false 10 | print($("HO" + "ME").is_empty()); // expect: false 11 | print($("INVALID").is_empty());// expect: true 12 | -------------------------------------------------------------------------------- /tests/integration/env/syntax.ai: -------------------------------------------------------------------------------- 1 | let a = $123; // Error at '123': Expect environment variable name after '$'. 2 | 3 | let b = $+a; // Error at '+': Expect environment variable name after '$'. 4 | -------------------------------------------------------------------------------- /tests/integration/env/undefined_variable.ai: -------------------------------------------------------------------------------- 1 | let env = $(a); // expect runtime error: Undefined variable 'a'. 2 | -------------------------------------------------------------------------------- /tests/integration/error_handling/cannot_define_error_handler_for_lambda.ai: -------------------------------------------------------------------------------- 1 | let lambda = |x| { 2 | x + 1 3 | } |err| { // Error at '|': Lambda doesn't support declare error handler. 4 | print(err); 5 | }; 6 | -------------------------------------------------------------------------------- /tests/integration/error_handling/error_signatures.ai: -------------------------------------------------------------------------------- 1 | class FooError! {} 2 | 3 | fn f1() -> {} // Error at '{': Expect type after '->'. 4 | 5 | fn f2() -> FooError! | int {} // Error at 'int': Only error types can be listed after return type. 6 | 7 | fn f3() -> int FooError! {} // Error at 'FooError!': Expected '|' before error type. 8 | 9 | fn f4() -> int, FooError! {} // Error at ',': Expected '|' to separate return type and error types, found ','. 10 | -------------------------------------------------------------------------------- /tests/integration/error_handling/last_error_block_expr_is_function_call_value.ai: -------------------------------------------------------------------------------- 1 | enum ArithError! { 2 | DivideZero, 3 | } 4 | 5 | fn divide(a, b) -> int | ArithError! { 6 | if b == 0 { 7 | raise ArithError!::DivideZero; 8 | } 9 | 10 | return a / b; 11 | } 12 | 13 | fn do_math1() { 14 | let v = divide(1, 0) |err| { 15 | let a = 1; 16 | print("error:", err); // expect: error: ArithError!::DivideZero 17 | 999 // The last expression is function call result 18 | }; 19 | print(v); // expect: 999 20 | } 21 | 22 | let x = do_math1(); 23 | print(x); // expect: nil 24 | 25 | fn do_math2() { 26 | let a = 0; 27 | print(a); // expect: 0 28 | let v = divide(1, 0) |err| { 29 | let a = 1; 30 | print("error:", err); // expect: error: ArithError!::DivideZero 31 | 999 // The last expression is function call result 32 | }; 33 | print(v); // expect: 999 34 | } 35 | 36 | let x = do_math2(); 37 | print(x); // expect: nil 38 | 39 | // Global variable case 40 | let v = divide(1, 0) |err| { 41 | let a = 1; 42 | print("error:", err); // expect: error: ArithError!::DivideZero 43 | 999 // The last expression is function call result 44 | }; 45 | print(v); // expect: 999 46 | -------------------------------------------------------------------------------- /tests/integration/error_handling/pipe_chain.ai: -------------------------------------------------------------------------------- 1 | class NumberTooLarge! {} 2 | 3 | fn raise_if_greater_than_10(array) -> int | NumberTooLarge! { 4 | for let i = 0; i < len(array); i += 1 { 5 | if array[i] > 10 { 6 | raise NumberTooLarge! {}; 7 | } 8 | } 9 | array 10 | } 11 | 12 | let arr = [1, 2, 3, 4, 5]; 13 | let result = arr |> map(|x| x * 1) |> raise_if_greater_than_10 |err| { 14 | print(err); 15 | 10 16 | }; 17 | print(result); // expect: [1, 2, 3, 4, 5] 18 | 19 | let result = arr |> map(|x| x * 3) |> raise_if_greater_than_10 |err| { 20 | print(err); // expect: NumberTooLarge! {} 21 | "greater_than_10" 22 | }; 23 | print(result); // expect: greater_than_10 24 | -------------------------------------------------------------------------------- /tests/integration/error_handling/propagation.ai: -------------------------------------------------------------------------------- 1 | enum ArithError! { 2 | DivideZero 3 | } 4 | class AnotherError! { 5 | msg: str = "another error", 6 | } 7 | 8 | fn divide(a, b) -> int | ArithError! { 9 | if b == 0 { 10 | raise ArithError!::DivideZero; 11 | } 12 | return a / b; 13 | } 14 | 15 | fn do_math1(n) -> ArithError! { 16 | // use ? to propagate error 17 | let v = divide(1, n)?; 18 | print("[value]", v); 19 | } 20 | 21 | do_math1(1); // expect: [value] 1 22 | print(do_math1(0)); // expect: ArithError!::DivideZero 23 | 24 | fn do_math2(n) -> ArithError! { 25 | // use raise to re-raise error 26 | let v = divide(1, n) |err| { 27 | raise err; 28 | }; 29 | print("[value]", v); 30 | } 31 | 32 | do_math2(1); // expect: [value] 1 33 | print(do_math2(0)); 34 | // expect: ArithError!::DivideZero 35 | 36 | fn do_math3(n) -> ArithError! { 37 | // use raise to re-raise error 38 | let v = divide(1, n) |err| { 39 | print(err); 40 | raise AnotherError!(); 41 | }; 42 | print("[value]", v); 43 | } 44 | 45 | do_math3(1); // expect: [value] 1 46 | print(do_math3(0)); 47 | // expect: ArithError!::DivideZero 48 | // expect: AnotherError! {msg: another error} 49 | -------------------------------------------------------------------------------- /tests/integration/error_handling/raise_catch.ai: -------------------------------------------------------------------------------- 1 | // Every enum or class ends with ! is an error type 2 | enum ArithError! { 3 | DivideZero = "Can not divide zero" 4 | } 5 | // Declare error type this function would throw 6 | // Multiple error types are supported, like this: 7 | // fn divide(a, b) -> int, ArithError! | AnotherError! {} 8 | fn divide(a, b) -> int | ArithError! { 9 | if b == 0 { 10 | raise ArithError!::DivideZero; 11 | } 12 | 13 | return a / b; 14 | } 15 | // Catch error and early return 16 | fn do_math1(n) { 17 | // use syntax `|err| {}` after function call to handle error 18 | let v = divide(1, n) |err| { 19 | print("[error]", err); 20 | return; 21 | }; 22 | print("[value]", v); 23 | } 24 | do_math1(1); // expect: [value] 1 25 | do_math1(0); // expect: [error] ArithError!::DivideZero(Can not divide zero) 26 | 27 | // Catch error and no early return 28 | fn do_math2(n) { 29 | let v = divide(1, n) |err| { 30 | print("[error]", err); 31 | }; 32 | print("[value]", v); 33 | } 34 | do_math2(1); // expect: [value] 1 35 | do_math2(0); 36 | // expect: [error] ArithError!::DivideZero(Can not divide zero) 37 | // expect: [value] nil 38 | -------------------------------------------------------------------------------- /tests/integration/error_handling/raise_error_in_lambda.ai: -------------------------------------------------------------------------------- 1 | class DivideZero! {} 2 | let divid = |x, y| { 3 | if y == 0 { 4 | raise DivideZero! {}; // Error at 'raise': Cannot raise error in lambda. 5 | } 6 | 7 | x / y 8 | }; -------------------------------------------------------------------------------- /tests/integration/error_handling/raise_outside_declared_function.ai: -------------------------------------------------------------------------------- 1 | class FooError! {} 2 | 3 | fn foo() { 4 | raise FooError! {}; // Error at 'raise': Cannot use 'raise' outside of a function that declares error types. 5 | } 6 | 7 | enum ArithError! { 8 | DivideZero 9 | } 10 | fn divide(a, b) -> int | ArithError! { 11 | if b == 0 { 12 | raise ArithError!::DivideZero; 13 | } 14 | return a / b; 15 | } 16 | fn do_math(n) { 17 | let v = divide(1, n)?; // Error at '?': Cannot use '?' operator in function that doesn't declare error types. 18 | print("[value]", v); 19 | } 20 | -------------------------------------------------------------------------------- /tests/integration/field/call_function_field.ai: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | fn bar(a, b) { 4 | print("bar"); 5 | print(a); 6 | print(b); 7 | } 8 | 9 | let foo = Foo(); 10 | foo.bar = bar; 11 | 12 | foo.bar(1, 2); 13 | // expect: bar 14 | // expect: 1 15 | // expect: 2 16 | -------------------------------------------------------------------------------- /tests/integration/field/call_nonfunction_field.ai: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | let foo = Foo(); 4 | foo.bar = "not fn"; 5 | 6 | foo.bar(); // expect runtime error: Can only call functions and classes. 7 | -------------------------------------------------------------------------------- /tests/integration/field/get_and_set_method.ai: -------------------------------------------------------------------------------- 1 | // Bound methods have identity equality. 2 | class Foo { 3 | fn method(self, a) { 4 | print("method"); 5 | print(a); 6 | } 7 | fn other(self, a) { 8 | print("other"); 9 | print(a); 10 | } 11 | } 12 | 13 | let foo = Foo(); 14 | let method = foo.method; 15 | 16 | // Setting a property shadows the instance method. 17 | foo.method = foo.other; 18 | foo.method(1); 19 | // expect: other 20 | // expect: 1 21 | 22 | // The old method handle still points to the original method. 23 | method(2); 24 | // expect: method 25 | // expect: 2 26 | -------------------------------------------------------------------------------- /tests/integration/field/get_on_bool.ai: -------------------------------------------------------------------------------- 1 | true.foo; // expect runtime error: Only instances have properties. 2 | -------------------------------------------------------------------------------- /tests/integration/field/get_on_class.ai: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | Foo.bar; // expect runtime error: Only instances have properties. 3 | -------------------------------------------------------------------------------- /tests/integration/field/get_on_function.ai: -------------------------------------------------------------------------------- 1 | fn foo() {} 2 | 3 | foo.bar; // expect runtime error: Only instances have properties. 4 | -------------------------------------------------------------------------------- /tests/integration/field/get_on_nil.ai: -------------------------------------------------------------------------------- 1 | nil.foo; // expect runtime error: Only instances have properties. 2 | -------------------------------------------------------------------------------- /tests/integration/field/get_on_num.ai: -------------------------------------------------------------------------------- 1 | 123.foo; // expect runtime error: Only instances have properties. 2 | -------------------------------------------------------------------------------- /tests/integration/field/get_on_string.ai: -------------------------------------------------------------------------------- 1 | "str".foo; // expect runtime error: Only instances have properties. 2 | -------------------------------------------------------------------------------- /tests/integration/field/method.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn bar(self, arg) { 3 | print(arg); 4 | } 5 | } 6 | 7 | let bar = Foo().bar; 8 | print("got method"); // expect: got method 9 | bar("arg"); // expect: arg 10 | -------------------------------------------------------------------------------- /tests/integration/field/method_binds_self.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn sayName(self, a) { 3 | print(self.name); 4 | print(a); 5 | } 6 | } 7 | 8 | let foo1 = Foo(); 9 | foo1.name = "foo1"; 10 | 11 | let foo2 = Foo(); 12 | foo2.name = "foo2"; 13 | 14 | // Store the method reference on another object. 15 | foo2.f = foo1.sayName; 16 | // Still retains original receiver. 17 | foo2.f(1); 18 | // expect: foo1 19 | // expect: 1 20 | -------------------------------------------------------------------------------- /tests/integration/field/on_instance.ai: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | let foo = Foo(); 4 | 5 | print(foo.bar = "bar value"); // expect: bar value 6 | print(foo.baz = "baz value"); // expect: baz value 7 | 8 | print(foo.bar); // expect: bar value 9 | print(foo.baz); // expect: baz value 10 | -------------------------------------------------------------------------------- /tests/integration/field/set_evaluation_order.ai: -------------------------------------------------------------------------------- 1 | undefined1.bar // expect runtime error: Undefined variable 'undefined1'. 2 | = undefined2; -------------------------------------------------------------------------------- /tests/integration/field/set_on_bool.ai: -------------------------------------------------------------------------------- 1 | true.foo = "value"; // expect runtime error: Only instances have fields. 2 | -------------------------------------------------------------------------------- /tests/integration/field/set_on_class.ai: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | Foo.bar = "value"; // expect runtime error: Only instances have fields. 3 | -------------------------------------------------------------------------------- /tests/integration/field/set_on_function.ai: -------------------------------------------------------------------------------- 1 | fn foo() {} 2 | 3 | foo.bar = "value"; // expect runtime error: Only instances have fields. 4 | -------------------------------------------------------------------------------- /tests/integration/field/set_on_nil.ai: -------------------------------------------------------------------------------- 1 | nil.foo = "value"; // expect runtime error: Only instances have fields. 2 | -------------------------------------------------------------------------------- /tests/integration/field/set_on_num.ai: -------------------------------------------------------------------------------- 1 | 123.foo = "value"; // expect runtime error: Only instances have fields. 2 | -------------------------------------------------------------------------------- /tests/integration/field/set_on_string.ai: -------------------------------------------------------------------------------- 1 | "str".foo = "value"; // expect runtime error: Only instances have fields. 2 | -------------------------------------------------------------------------------- /tests/integration/field/undefined.ai: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | let foo = Foo(); 3 | 4 | foo.bar; // expect runtime error: Undefined property 'bar'. 5 | -------------------------------------------------------------------------------- /tests/integration/for/class_in_body.ai: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'class': Expect expression. 2 | for ;; class Foo {} 3 | -------------------------------------------------------------------------------- /tests/integration/for/closure_in_body.ai: -------------------------------------------------------------------------------- 1 | let f1; 2 | let f2; 3 | let f3; 4 | 5 | for let i = 1; i < 4; i = i + 1 { 6 | let j = i; 7 | fn f() { 8 | print(i); 9 | print(j); 10 | } 11 | 12 | if j == 1 { f1 = f; } 13 | else if j == 2 { f2 = f; } 14 | else { f3 = f; } 15 | } 16 | 17 | f1(); // expect: 4 18 | // expect: 1 19 | f2(); // expect: 4 20 | // expect: 2 21 | f3(); // expect: 4 22 | // expect: 3 23 | -------------------------------------------------------------------------------- /tests/integration/for/fun_in_body.ai: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'fn': Expect expression. 2 | for ;; fn foo() {} 3 | -------------------------------------------------------------------------------- /tests/integration/for/object_literal.ai: -------------------------------------------------------------------------------- 1 | // Object literal in increment part should error 2 | for let i = 0; i < 3; {a:1} { // Error at ':': Expect ';' after expression. 3 | print(i); 4 | i += 1; 5 | } 6 | 7 | for let i = 0; i < 3; {} { // Error at '{': Empty object literal not allowed in for loop increment 8 | print(i); 9 | i += 1; 10 | } -------------------------------------------------------------------------------- /tests/integration/for/return_closure.ai: -------------------------------------------------------------------------------- 1 | fn f() { 2 | for ;; { 3 | let i = "i"; 4 | fn g() { print(i); } 5 | return g; 6 | } 7 | } 8 | 9 | let h = f(); 10 | h(); // expect: i 11 | -------------------------------------------------------------------------------- /tests/integration/for/return_inside.ai: -------------------------------------------------------------------------------- 1 | fn f() { 2 | for ;; { 3 | let i = "i"; 4 | return i; 5 | } 6 | } 7 | 8 | print(f()); 9 | // expect: i 10 | -------------------------------------------------------------------------------- /tests/integration/for/scope.ai: -------------------------------------------------------------------------------- 1 | { 2 | let i = "before"; 3 | 4 | // New variable is in inner scope. 5 | for let i = 0; i < 1; i = i + 1 { 6 | print(i); // expect: 0 7 | 8 | // Loop body is in second inner scope. 9 | let i = -1; 10 | print(i); // expect: -1 11 | } 12 | } 13 | 14 | { 15 | // New variable shadows outer variable. 16 | for let i = 0; i > 0; i = i + 1 {} 17 | 18 | // Goes out of scope after loop. 19 | let i = "after"; 20 | print(i); // expect: after 21 | 22 | // Can reuse an existing variable. 23 | for i = 0; i < 1; i = i + 1 { 24 | print(i); // expect: 0 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /tests/integration/for/statement_condition.ai: -------------------------------------------------------------------------------- 1 | for let a = 1; {}; a = a + 1 { 2 | print(a); // expect: 1 3 | break; 4 | } 5 | -------------------------------------------------------------------------------- /tests/integration/for/statement_initializer.ai: -------------------------------------------------------------------------------- 1 | let a = 1; 2 | for {}; a < 2; a = a + 1 { 3 | print(a); // expect: 1 4 | } 5 | -------------------------------------------------------------------------------- /tests/integration/for/syntax.ai: -------------------------------------------------------------------------------- 1 | // Single-expression body. 2 | for let c = 0; c < 3; { 3 | c += 1; 4 | print(c); 5 | } 6 | // expect: 1 7 | // expect: 2 8 | // expect: 3 9 | 10 | // Block body. 11 | for let a = 0; a < 3; a = a + 1 { 12 | print(a); 13 | } 14 | // expect: 0 15 | // expect: 1 16 | // expect: 2 17 | 18 | // No clauses. 19 | fn foo() { 20 | for ;; { 21 | return "done"; 22 | } 23 | } 24 | print(foo()); // expect: done 25 | 26 | // No variable. 27 | let i = 0; 28 | for ; i < 2; i = i + 1 { 29 | print(i); 30 | } 31 | // expect: 0 32 | // expect: 1 33 | 34 | // No condition. 35 | fn bar() { 36 | for let i = 0;; i = i + 1 { 37 | print(i); 38 | if i >= 2 { return; } 39 | } 40 | } 41 | bar(); 42 | // expect: 0 43 | // expect: 1 44 | // expect: 2 45 | 46 | // No increment. 47 | for let i = 0; i < 2; { 48 | print(i); 49 | i = i + 1; 50 | } 51 | // expect: 0 52 | // expect: 1 53 | 54 | // Statement bodies. 55 | for ; false; { 56 | if true { 1; } else { 2; } 57 | } 58 | for ; false; { 59 | while true { 1; } 60 | } 61 | for ; false; { 62 | for ;; { 63 | 1; 64 | } 65 | } 66 | 67 | // Object literals are fine in initializer and condition parts 68 | for let obj = {a:1}; len(obj) > 0; { // This is OK - semicolons make it unambiguous 69 | print("hi"); // expect: hi 70 | break; 71 | } -------------------------------------------------------------------------------- /tests/integration/for/var_in_body.ai: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'let': Expect expression. 2 | for ;; let foo; 3 | -------------------------------------------------------------------------------- /tests/integration/fstring/basic.ai: -------------------------------------------------------------------------------- 1 | // Basic string interpolation 2 | let name = "Alice"; 3 | let greeting = f"Hello, {name}!"; 4 | print(greeting); // expect: Hello, Alice! 5 | 6 | // Multiple variables 7 | let first = "Bob"; 8 | let last = "Smith"; 9 | print(f"{first} {last}"); // expect: Bob Smith 10 | 11 | // Empty f-string 12 | let empty = f""; 13 | print(empty); // expect: "" 14 | 15 | // F-string with no interpolation 16 | let no_interp = f"Just a regular string"; 17 | print(no_interp); // expect: Just a regular string 18 | 19 | // Multiple interpolations 20 | let a = "one"; 21 | let b = "two"; 22 | let c = "three"; 23 | print(f"{a}, {b}, {c}"); // expect: one, two, three 24 | 25 | // Interpolations at the beginning, middle, and end 26 | print(f"{a} is first"); // expect: one is first 27 | print(f"The middle is {b}"); // expect: The middle is two 28 | print(f"Last is {c}"); // expect: Last is three 29 | -------------------------------------------------------------------------------- /tests/integration/fstring/edge_cases.ai: -------------------------------------------------------------------------------- 1 | // Tests edge cases for f-strings 2 | 3 | // Escaped braces 4 | print(f"Escaped opening brace: \\{"); // expect: Escaped opening brace: \{ 5 | print(f"Escaped closing brace: \\}"); // expect: Escaped closing brace: \} 6 | 7 | // Empty expressions (should be handled by parser error) 8 | // print(f"Empty expression: {}"); // This should cause an error 9 | 10 | // Adjacent expressions 11 | let a = "first"; 12 | let b = "second"; 13 | print(f"{a}{b}"); // expect: firstsecond 14 | 15 | // Nested quotes 16 | print(f"She said, \"Hello, {a}!\""); // expect: She said, "Hello, first!" 17 | print(f"Single quotes: \'{b}\'"); // expect: Single quotes: 'second' 18 | 19 | // Special characters inside string 20 | print(f"Tab: \t Newline: \n with {a}"); // expect: Tab: Newline: 21 | // expect: with first 22 | 23 | // Unicode characters 24 | let emoji = "😊"; 25 | print(f"Unicode: {emoji}"); // expect: Unicode: 😊 26 | 27 | // Nested expressions (might be tricky for the parser) 28 | let c = 10; 29 | print(f"Nested: {c + (c * 2)}"); // expect: Nested: 30 30 | 31 | // Conditional expressions 32 | // TODO: support quote escape in fstring 33 | // let isAdmin = true; 34 | // print(f"User is {"admin" if isAdmin else "regular user"}"); 35 | 36 | // Multiple expressions with no spaces 37 | print(f"{a}{b}{c}"); // expect: firstsecond10 38 | -------------------------------------------------------------------------------- /tests/integration/fstring/expression.ai: -------------------------------------------------------------------------------- 1 | // Numeric expressions 2 | let x = 10; 3 | let y = 5; 4 | print(f"{x} + {y} = {x + y}"); // expect: 10 + 5 = 15 5 | print(f"{x} - {y} = {x - y}"); // expect: 10 - 5 = 5 6 | print(f"{x} * {y} = {x * y}"); // expect: 10 * 5 = 50 7 | print(f"{x} / {y} = {x / y}"); // expect: 10 / 5 = 2 8 | 9 | // Complex expressions 10 | print(f"Complex: {(x + y) * 2 - (x / y)}"); // expect: Complex: 28 11 | 12 | // Boolean expressions 13 | let isTrue = true; 14 | let isFalse = false; 15 | print(f"Is it true? {isTrue}"); // expect: Is it true? true 16 | print(f"Is it false? {isFalse}"); // expect: Is it false? false 17 | print(f"Logical AND: {isTrue and isFalse}"); // expect: Logical AND: false 18 | print(f"Logical OR: {isTrue or isFalse}"); // expect: Logical OR: true 19 | 20 | // String operations 21 | // let str1 = "hello"; 22 | // let str2 = "world"; 23 | // print(f"Concatenated: {str1 + \" \" + str2}"); 24 | 25 | // Function calls in interpolation 26 | fn get_message() { 27 | return "function result"; 28 | } 29 | 30 | print(f"From function: {get_message()}"); // expect: From function: function result -------------------------------------------------------------------------------- /tests/integration/fstring/types.ai: -------------------------------------------------------------------------------- 1 | // Tests f-strings with different data types 2 | 3 | // Numbers 4 | let integer = 42; 5 | let float = 3.14; 6 | print(f"Integer: {integer}"); // expect: Integer: 42 7 | print(f"Float: {float}"); // expect: Float: 3.14 8 | 9 | // Boolean 10 | let truth = true; 11 | let falsehood = false; 12 | print(f"Truth: {truth}"); // expect: Truth: true 13 | print(f"Falsehood: {falsehood}"); // expect: Falsehood: false 14 | 15 | // Nil 16 | let nothing = nil; 17 | print(f"Nothing: {nothing}"); // expect: Nothing: nil 18 | 19 | // Arrays 20 | let array = [1, 2, 3]; 21 | print(f"Array: {array}"); // expect: Array: [1, 2, 3] 22 | 23 | // Objects 24 | let obj = {"name": "Dave", "age": 30}; 25 | print(f"{obj.name}"); // expect: Dave 26 | print(f"{obj.age}"); // expect: 30 27 | 28 | // Nested objects and arrays 29 | let nested = {"items": [1, 2, {"key": "value"}]}; 30 | print(f"Nested: {nested}"); // expect: Nested: {items: [1, 2, {key: value}]} 31 | 32 | // Enum 33 | enum Status { 34 | Active, 35 | Inactive, 36 | Pending 37 | } 38 | print(f"Enum value: {Status::Active}"); // expect: Enum value: Status::Active 39 | 40 | // Class instance 41 | class Person { 42 | fn new(name, age) { 43 | self.name = name; 44 | self.age = age; 45 | } 46 | } 47 | let person = Person("Emma", 25); 48 | print(f"Person: {person}"); // expect: Person: -------------------------------------------------------------------------------- /tests/integration/function/body_must_be_block.ai: -------------------------------------------------------------------------------- 1 | fn f() 123; // Error at '123': Expect '{' before function body. 2 | -------------------------------------------------------------------------------- /tests/integration/function/duplicate_fn.ai: -------------------------------------------------------------------------------- 1 | fn test() { 2 | } 3 | 4 | fn test(x: str, y: str) -> str { // Error at 'test': A function with same name already exists. 5 | } -------------------------------------------------------------------------------- /tests/integration/function/empty_body.ai: -------------------------------------------------------------------------------- 1 | fn f() {} 2 | print(f()); // expect: nil 3 | -------------------------------------------------------------------------------- /tests/integration/function/expect_type_after_arrow.ai: -------------------------------------------------------------------------------- 1 | fn test() -> { // Error at '{': Expect type after '->'. 2 | } 3 | -------------------------------------------------------------------------------- /tests/integration/function/extra_arguments.ai: -------------------------------------------------------------------------------- 1 | fn f(a, b) { 2 | print(a); 3 | print(b); 4 | } 5 | 6 | f(1, 2, 3, 4); // expect runtime error: Expected 2 arguments but got 4. 7 | -------------------------------------------------------------------------------- /tests/integration/function/invalid_parameter_type.ai: -------------------------------------------------------------------------------- 1 | fn a(x: 123) { // Error at '123': Invalid type annotation. 2 | } -------------------------------------------------------------------------------- /tests/integration/function/last_expr_implicit_return.ai: -------------------------------------------------------------------------------- 1 | fn f0() { 2 | true 3 | } 4 | fn f1() { 5 | "hi" 6 | } 7 | fn f2() { 8 | 1 + 2 * 3 - 4 9 | } 10 | fn f3() { 11 | let a = "hi"; 12 | a 13 | } 14 | fn f4() { 15 | let a = "hi"; 16 | [a, 1] 17 | } 18 | fn f5(x) { 19 | || x + 1 20 | } 21 | fn f6() { 22 | print("f6"); 23 | } 24 | 25 | print(f0()); // expect: true 26 | print(f1()); // expect: hi 27 | print(f2()); // expect: 3 28 | print(f3()); // expect: hi 29 | print(f4()); // expect: [hi, 1] 30 | print(f5(1)()); // expect: 2 31 | print(f6()); 32 | // expect: f6 33 | // expect: nil 34 | -------------------------------------------------------------------------------- /tests/integration/function/local_mutual_recursion.ai: -------------------------------------------------------------------------------- 1 | { 2 | fn isEven(n) { 3 | if n == 0 { return true; } 4 | return isOdd(n - 1); // expect runtime error: Undefined variable 'isOdd'. 5 | } 6 | 7 | fn isOdd(n) { 8 | if n == 0 { return false; } 9 | return isEven(n - 1); 10 | } 11 | 12 | isEven(4); 13 | } -------------------------------------------------------------------------------- /tests/integration/function/local_recursion.ai: -------------------------------------------------------------------------------- 1 | { 2 | fn fib(n) { 3 | if n < 2 { return n; } 4 | return fib(n - 1) + fib(n - 2); 5 | } 6 | 7 | print(fib(8)); // expect: 21 8 | } 9 | -------------------------------------------------------------------------------- /tests/integration/function/missing_arguments.ai: -------------------------------------------------------------------------------- 1 | fn f(a, b) {} 2 | 3 | f(1); // expect runtime error: Expected 2 arguments but got 1. 4 | -------------------------------------------------------------------------------- /tests/integration/function/missing_comma_in_parameters.ai: -------------------------------------------------------------------------------- 1 | // [line 3] Error at 'c': Expect ')' after parameters. 2 | // [c line 4] Error at end: Expect '}' after block. 3 | fn foo(a, b c, d, e, f) {} 4 | -------------------------------------------------------------------------------- /tests/integration/function/mutual_recursion.ai: -------------------------------------------------------------------------------- 1 | fn isEven(n) { 2 | if n == 0 { return true; } 3 | return isOdd(n - 1); 4 | } 5 | 6 | fn isOdd(n) { 7 | if n == 0 { return false; } 8 | return isEven(n - 1); 9 | } 10 | 11 | print(isEven(4)); // expect: true 12 | print(isOdd(3)); // expect: true 13 | -------------------------------------------------------------------------------- /tests/integration/function/nested_call_with_arguments.ai: -------------------------------------------------------------------------------- 1 | fn returnArg(arg) { 2 | return arg; 3 | } 4 | 5 | fn returnFunCallWithArg(func, arg) { 6 | return returnArg(func)(arg); 7 | } 8 | 9 | fn printArg(arg) { 10 | print(arg); 11 | } 12 | 13 | returnFunCallWithArg(printArg, "hello world"); // expect: hello world 14 | -------------------------------------------------------------------------------- /tests/integration/function/parameters.ai: -------------------------------------------------------------------------------- 1 | fn f0() { return 0; } 2 | print(f0()); // expect: 0 3 | 4 | fn f1(a) { return a; } 5 | print(f1(1)); // expect: 1 6 | 7 | fn f2(a, b) { return a + b; } 8 | print(f2(1, 2)); // expect: 3 9 | 10 | fn f3(a, b, c) { return a + b + c; } 11 | print(f3(1, 2, 3)); // expect: 6 12 | 13 | fn f4(a, b, c, d) { return a + b + c + d; } 14 | print(f4(1, 2, 3, 4)); // expect: 10 15 | 16 | fn f5(a, b, c, d, e) { return a + b + c + d + e; } 17 | print(f5(1, 2, 3, 4, 5)); // expect: 15 18 | 19 | fn f6(a, b, c, d, e, f) { return a + b + c + d + e + f; } 20 | print(f6(1, 2, 3, 4, 5, 6)); // expect: 21 21 | 22 | fn f7(a, b, c, d, e, f, g) { return a + b + c + d + e + f + g; } 23 | print(f7(1, 2, 3, 4, 5, 6, 7)); // expect: 28 24 | 25 | fn f8(a, b, c, d, e, f, g, h) { return a + b + c + d + e + f + g + h; } 26 | print(f8(1, 2, 3, 4, 5, 6, 7, 8)); // expect: 36 27 | -------------------------------------------------------------------------------- /tests/integration/function/paramter_return_types.ai: -------------------------------------------------------------------------------- 1 | class A {} 2 | 3 | enum B {} 4 | 5 | fn f1(a: A) {} 6 | fn f2() -> A {} 7 | fn f3(a: A) -> A {} 8 | 9 | fn f4(b: B) {} 10 | fn f5() -> B {} 11 | fn f6(b: B) -> B {} -------------------------------------------------------------------------------- /tests/integration/function/print.ai: -------------------------------------------------------------------------------- 1 | fn foo() {} 2 | print(foo); // expect: 3 | 4 | print(len); // expect: 5 | -------------------------------------------------------------------------------- /tests/integration/function/recursion.ai: -------------------------------------------------------------------------------- 1 | fn fib(n) { 2 | if n < 2 { return n; } 3 | return fib(n - 1) + fib(n - 2); 4 | } 5 | 6 | print(fib(8)); // expect: 21 7 | -------------------------------------------------------------------------------- /tests/integration/function/undefined_parameter_type.ai: -------------------------------------------------------------------------------- 1 | fn test( 2 | x: vv, // Error at 'vv': Undefined type 'vv'. 3 | y: str, 4 | ) -> str { 5 | } -------------------------------------------------------------------------------- /tests/integration/function/undefined_return_type.ai: -------------------------------------------------------------------------------- 1 | fn test( 2 | x: str, 3 | y: str, 4 | ) -> vv { // Error at 'vv': Undefined type 'vv'. 5 | } -------------------------------------------------------------------------------- /tests/integration/helloworld.ai: -------------------------------------------------------------------------------- 1 | print("Hello World"); // expect: Hello World -------------------------------------------------------------------------------- /tests/integration/if/dangling_else.ai: -------------------------------------------------------------------------------- 1 | // A dangling else binds to the right-most if. 2 | if true { 3 | if false { 4 | print("bad"); 5 | } else { 6 | print("good"); // expect: good 7 | } 8 | } 9 | if false { 10 | if true { 11 | print("bad"); 12 | } else { 13 | print("bad"); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /tests/integration/if/else.ai: -------------------------------------------------------------------------------- 1 | // Evaluate the 'else' expression if the condition is false. 2 | if true { 3 | print("good"); 4 | } else { 5 | print("bad"); // expect: good 6 | } 7 | if false { 8 | print("bad"); 9 | } else { 10 | print("good"); // expect: good 11 | } 12 | 13 | // Allow block body. 14 | if false { 15 | nil; 16 | } else { 17 | print("block"); // expect: block 18 | } 19 | -------------------------------------------------------------------------------- /tests/integration/if/if.ai: -------------------------------------------------------------------------------- 1 | // Evaluate the 'then' expression if the condition is true. 2 | if true { print("good"); } // expect: good 3 | if false { print("bad"); } 4 | 5 | // Allow block body. 6 | if true { print("block"); } // expect: block 7 | 8 | // Assignment in if condition. 9 | let a = false; 10 | if a = true { print(a); } // expect: true 11 | -------------------------------------------------------------------------------- /tests/integration/if/missing_close_paren.ai: -------------------------------------------------------------------------------- 1 | if true { print("block"); // [line 2] Error at end: Expect '}' after block. 2 | -------------------------------------------------------------------------------- /tests/integration/if/missing_open_paren.ai: -------------------------------------------------------------------------------- 1 | if true print("block"); } // Error at 'print': Expect '{' before then branch. -------------------------------------------------------------------------------- /tests/integration/if/object_literal_in_condition.ai: -------------------------------------------------------------------------------- 1 | if len({}) > 5 { // Error at '{': Cannot use object literals in flow control conditions 2 | // ... 3 | } else if len({}) > 5 { // Error at '{': Cannot use object literals in flow control conditions 4 | // ... 5 | } -------------------------------------------------------------------------------- /tests/integration/if/truth.ai: -------------------------------------------------------------------------------- 1 | // False and nil are false. 2 | if false { print("bad"); } else { print("false"); } // expect: false 3 | if nil { print("bad"); } else { print("nil"); } // expect: nil 4 | 5 | // Everything else is true. 6 | if true { print(true); } // expect: true 7 | if 0 { print(0); } // expect: 0 8 | if "" { print("empty"); } // expect: empty 9 | -------------------------------------------------------------------------------- /tests/integration/in/invalid_array_key.ai: -------------------------------------------------------------------------------- 1 | 1 in "not an array"; // expect runtime error: Right operand of 'in' operator must be array or object. -------------------------------------------------------------------------------- /tests/integration/in/invalid_object_key.ai: -------------------------------------------------------------------------------- 1 | 42 in {a: 1}; // expect runtime error: Object key must be a string in 'in' operator. -------------------------------------------------------------------------------- /tests/integration/in/normal.ai: -------------------------------------------------------------------------------- 1 | let arr = [1, 2, 3, 4]; 2 | print(1 in arr); // expect: true 3 | print(5 in arr); // expect: false 4 | let key = 3; 5 | print(key in arr); // expect: true 6 | 7 | let obj = {a: "value"}; 8 | print("a" in obj); // expect: true 9 | print("b" in obj); // expect: false 10 | let key = "key"; 11 | print(key in arr); // expect: false 12 | -------------------------------------------------------------------------------- /tests/integration/in/string_in_string.ai: -------------------------------------------------------------------------------- 1 | "a" in "not an array"; // expect runtime error: Right operand of 'in' operator must be array or object. -------------------------------------------------------------------------------- /tests/integration/inheritance/constructor.ai: -------------------------------------------------------------------------------- 1 | class A { 2 | fn new(param) { 3 | self.field = param; 4 | } 5 | 6 | fn test(self) { 7 | print(self.field); 8 | } 9 | } 10 | 11 | class B(A) {} 12 | 13 | let b = B("value"); 14 | b.test(); // expect: value 15 | -------------------------------------------------------------------------------- /tests/integration/inheritance/inherit_from_function.ai: -------------------------------------------------------------------------------- 1 | fn foo() {} 2 | 3 | class Subclass(foo) {} // expect runtime error: Superclass must be a class. 4 | -------------------------------------------------------------------------------- /tests/integration/inheritance/inherit_from_nil.ai: -------------------------------------------------------------------------------- 1 | let Nil = nil; 2 | class Foo(Nil) {} // expect runtime error: Superclass must be a class. 3 | -------------------------------------------------------------------------------- /tests/integration/inheritance/inherit_from_number.ai: -------------------------------------------------------------------------------- 1 | let Number = 123; 2 | class Foo(Number) {} // expect runtime error: Superclass must be a class. 3 | -------------------------------------------------------------------------------- /tests/integration/inheritance/inherit_methods.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn methodOnFoo(self) { print("foo"); } 3 | fn override(self) { print("foo"); } 4 | } 5 | 6 | class Bar(Foo) { 7 | fn methodOnBar(self) { print("bar"); } 8 | fn override(self) { print("bar"); } 9 | } 10 | 11 | let bar = Bar(); 12 | bar.methodOnFoo(); // expect: foo 13 | bar.methodOnBar(); // expect: bar 14 | bar.override(); // expect: bar 15 | -------------------------------------------------------------------------------- /tests/integration/inheritance/inherit_missing_close_paren.ai: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | class Bar(Foo {} // Error at '{': Expect ')' after superclass name 4 | -------------------------------------------------------------------------------- /tests/integration/inheritance/parenthesized_superclass.ai: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | // [line 4] Error at '(': Expect superclass name. 4 | class Bar((Foo) {} 5 | -------------------------------------------------------------------------------- /tests/integration/inheritance/set_fields_from_base_class.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn foo(self, a, b) { 3 | self.field1 = a; 4 | self.field2 = b; 5 | } 6 | 7 | fn fooPrint(self) { 8 | print(self.field1); 9 | print(self.field2); 10 | } 11 | } 12 | 13 | class Bar(Foo) { 14 | fn bar(self, a, b) { 15 | self.field1 = a; 16 | self.field2 = b; 17 | } 18 | 19 | fn barPrint(self) { 20 | print(self.field1); 21 | print(self.field2); 22 | } 23 | } 24 | 25 | let bar = Bar(); 26 | bar.foo("foo 1", "foo 2"); 27 | bar.fooPrint(); 28 | // expect: foo 1 29 | // expect: foo 2 30 | 31 | bar.bar("bar 1", "bar 2"); 32 | bar.barPrint(); 33 | // expect: bar 1 34 | // expect: bar 2 35 | 36 | bar.fooPrint(); 37 | // expect: bar 1 38 | // expect: bar 2 39 | -------------------------------------------------------------------------------- /tests/integration/inline_if/invalid_syntax.ai: -------------------------------------------------------------------------------- 1 | // Missing else 2 | let x = true if 1 > 0; // Error at ';': Expect 'else' after inline if condition. 3 | 4 | // Missing condition 5 | let x = 1 if else 2; // Error at 'else': Expect expression. 6 | 7 | // Empty branches 8 | let x = if true else; // Error at 'if': Expect expression. 9 | 10 | // Invalid order 11 | let x = if true 1 else 2; // Error at 'if': Expect expression. 12 | 13 | // Incomplete 14 | let x = true if; // Error at ';': Expect expression. 15 | 16 | // Missing then branch 17 | let x = if true else 1; // Error at 'if': Expect expression. 18 | 19 | // Double else 20 | let x = 1 if true else 2 else 3; // Error at 'else': Expect ';' after variable declaration. -------------------------------------------------------------------------------- /tests/integration/inline_if/normal.ai: -------------------------------------------------------------------------------- 1 | // Basic test 2 | let x = 1; 3 | let y = true if x > 0 else false; 4 | print(y); // expect: true 5 | 6 | let a = -1; 7 | let b = "positive" if a > 0 else "negative"; 8 | print(b); // expect: negative 9 | 10 | // Nested inline if 11 | let score = 75; 12 | let grade = "A" if score >= 90 else "B" if score >= 80 else "C" if score >= 70 else "F"; 13 | print(grade); // expect: C 14 | 15 | // With expressions 16 | let n1 = 5; 17 | let n2 = 10; 18 | let max = n1 if n1 > n2 else n2; 19 | print(max); // expect: 10 20 | 21 | // With function calls 22 | fn get_num() { return 42; } 23 | let result = "big" if get_num() > 40 else "small"; 24 | print(result); // expect: big 25 | 26 | // With arithmetic 27 | let val = 2 + 3 if true else 5 + 5; 28 | print(val); // expect: 5 29 | 30 | // Test precedence 31 | let x = 1 + 2 if true else 3 + 4; 32 | print(x); // expect: 3 33 | 34 | // Test with boolean operations 35 | let a = true; 36 | let b = false; 37 | let result = "yes" if a and b else "no"; 38 | print(result); // expect: no 39 | 40 | // Test with string comparison 41 | let name = "Bob"; 42 | let greeting = "Hello, Bob" if name == "Bob" else "Hello, stranger"; 43 | print(greeting); // expect: Hello, Bob -------------------------------------------------------------------------------- /tests/integration/lambda/last_expr_implicit_return.ai: -------------------------------------------------------------------------------- 1 | let p = |x| {}; 2 | print(p(100)); // expect: nil 3 | 4 | let p = |x| { 5 | print(x); 6 | }; 7 | print(p(100)); 8 | // expect: 100 9 | // expect: nil 10 | 11 | let p = |x| { 12 | print(x); 13 | 99; 14 | }; 15 | print(p(100)); 16 | // expect: 100 17 | // expect: nil 18 | 19 | let p = |x| { 20 | print(x); 21 | 99 22 | }; 23 | print(p(100)); 24 | // expect: 100 25 | // expect: 99 26 | 27 | let p = |x| { 28 | print(x); 29 | return 99; 30 | }; 31 | print(p(100)); 32 | // expect: 100 33 | // expect: 99 -------------------------------------------------------------------------------- /tests/integration/lambda/syntax.ai: -------------------------------------------------------------------------------- 1 | let x = |x x + 1; // Error at 'x': Expect '|' after lambda parameters. 2 | 3 | let y = |x| { 4 | return x; 5 | // Error at end: Expect '}' after block. -------------------------------------------------------------------------------- /tests/integration/lambda/upvalue_captures.ai: -------------------------------------------------------------------------------- 1 | 2 | // Multiple lambdas capturing same variable 3 | let x = 0; 4 | let add_one = || { x = x + 1; return x; }; 5 | let get_x = || x; 6 | print(add_one()); // expect: 1 7 | print(get_x()); // expect: 1 8 | print(add_one()); // expect: 2 9 | print(get_x()); // expect: 2 10 | 11 | 12 | // Capture in block lambda 13 | let multiplier = 2; 14 | let multiply = |x| { 15 | let result = x * multiplier; 16 | return result; 17 | }; 18 | print(multiply(5)); // expect: 10 19 | 20 | // Capture across function boundary 21 | fn make_doubler(factor) { 22 | return |x| x * factor; 23 | } 24 | let double = make_doubler(2); 25 | let triple = make_doubler(3); 26 | print(double(5)); // expect: 10 27 | print(triple(5)); // expect: 15 28 | 29 | // Deep nesting with multiple captures 30 | let make_adder = |x| { 31 | let base = x; 32 | return |y| { 33 | let inner_base = base; 34 | return |z| inner_base + y + z; 35 | }; 36 | }; 37 | let add_five = make_adder(5); 38 | let add_five_two = add_five(2); 39 | print(add_five_two(3)); // expect: 10 -------------------------------------------------------------------------------- /tests/integration/limit/no_reuse_constants.ai: -------------------------------------------------------------------------------- 1 | fn f() { 2 | 0; 1; 2; 3; 4; 5; 6; 7; 3 | 8; 9; 10; 11; 12; 13; 14; 15; 4 | 16; 17; 18; 19; 20; 21; 22; 23; 5 | 24; 25; 26; 27; 28; 29; 30; 31; 6 | 32; 33; 34; 35; 36; 37; 38; 39; 7 | 40; 41; 42; 43; 44; 45; 46; 47; 8 | 48; 49; 50; 51; 52; 53; 54; 55; 9 | 56; 57; 58; 59; 60; 61; 62; 63; 10 | 64; 65; 66; 67; 68; 69; 70; 71; 11 | 72; 73; 74; 75; 76; 77; 78; 79; 12 | 80; 81; 82; 83; 84; 85; 86; 87; 13 | 88; 89; 90; 91; 92; 93; 94; 95; 14 | 96; 97; 98; 99; 100; 101; 102; 103; 15 | 104; 105; 106; 107; 108; 109; 110; 111; 16 | 112; 113; 114; 115; 116; 117; 118; 119; 17 | 120; 121; 122; 123; 124; 125; 126; 127; 18 | 128; 129; 130; 131; 132; 133; 134; 135; 19 | 136; 137; 138; 139; 140; 141; 142; 143; 20 | 144; 145; 146; 147; 148; 149; 150; 151; 21 | 152; 153; 154; 155; 156; 157; 158; 159; 22 | 160; 161; 162; 163; 164; 165; 166; 167; 23 | 168; 169; 170; 171; 172; 173; 174; 175; 24 | 176; 177; 178; 179; 180; 181; 182; 183; 25 | 184; 185; 186; 187; 188; 189; 190; 191; 26 | 192; 193; 194; 195; 196; 197; 198; 199; 27 | 200; 201; 202; 203; 204; 205; 206; 207; 28 | 208; 209; 210; 211; 212; 213; 214; 215; 29 | 216; 217; 218; 219; 220; 221; 222; 223; 30 | 224; 225; 226; 227; 228; 229; 230; 231; 31 | 232; 233; 234; 235; 236; 237; 238; 239; 32 | 240; 241; 242; 243; 244; 245; 246; 247; 33 | 248; 249; 250; 251; 252; 253; 254; 255; 34 | 35 | 1; // Error at '1': Too many constants in one chunk. 36 | } 37 | -------------------------------------------------------------------------------- /tests/integration/limit/stack_overflow.ai: -------------------------------------------------------------------------------- 1 | fn foo() { 2 | let a1; 3 | let a2; 4 | let a3; 5 | let a4; 6 | let a5; 7 | let a6; 8 | let a7; 9 | let a8; 10 | let a9; 11 | let a10; 12 | let a11; 13 | let a12; 14 | let a13; 15 | let a14; 16 | let a15; 17 | let a16; 18 | foo(); // expect runtime error: Stack overflow. 19 | } 20 | 21 | foo(); 22 | -------------------------------------------------------------------------------- /tests/integration/logical_operator/and.ai: -------------------------------------------------------------------------------- 1 | // Note: These tests implicitly depend on ints being truthy. 2 | 3 | // Return the first non-true argument. 4 | print(false and 1); // expect: false 5 | print(true and 1); // expect: 1 6 | print(1 and 2 and false); // expect: false 7 | 8 | // Return the last argument if all are true. 9 | print(1 and true); // expect: true 10 | print(1 and 2 and 3); // expect: 3 11 | 12 | // Short-circuit at the first false argument. 13 | let a = "before"; 14 | let b = "before"; 15 | (a = true) and 16 | (b = false) and 17 | (a = "bad"); 18 | print(a); // expect: true 19 | print(b); // expect: false 20 | -------------------------------------------------------------------------------- /tests/integration/logical_operator/and_truth.ai: -------------------------------------------------------------------------------- 1 | // False and nil are false. 2 | print(false and "bad"); // expect: false 3 | print(nil and "bad"); // expect: nil 4 | 5 | // Everything else is true. 6 | print(true and "ok"); // expect: ok 7 | print(0 and "ok"); // expect: ok 8 | print("" and "ok"); // expect: ok 9 | -------------------------------------------------------------------------------- /tests/integration/logical_operator/or.ai: -------------------------------------------------------------------------------- 1 | // Note: These tests implicitly depend on ints being truthy. 2 | 3 | // Return the first true argument. 4 | print(1 or true); // expect: 1 5 | print(false or 1); // expect: 1 6 | print(false or false or true); // expect: true 7 | 8 | // Return the last argument if all are false. 9 | print(false or false); // expect: false 10 | print(false or false or false); // expect: false 11 | 12 | // Short-circuit at the first true argument. 13 | let a = "before"; 14 | let b = "before"; 15 | (a = false) or 16 | (b = true) or 17 | (a = "bad"); 18 | print(a); // expect: false 19 | print(b); // expect: true 20 | -------------------------------------------------------------------------------- /tests/integration/logical_operator/or_truth.ai: -------------------------------------------------------------------------------- 1 | // False and nil are false. 2 | print(false or "ok"); // expect: ok 3 | print(nil or "ok"); // expect: ok 4 | 5 | // Everything else is true. 6 | print(true or "ok"); // expect: true 7 | print(0 or "ok"); // expect: 0 8 | print("s" or "ok"); // expect: s 9 | -------------------------------------------------------------------------------- /tests/integration/match/bool.ai: -------------------------------------------------------------------------------- 1 | match true { 2 | true => print("true"), 3 | false => print("false"), 4 | }; 5 | match false { 6 | true => print("true"), 7 | false => print("false"), 8 | }; 9 | // expect: true 10 | // expect: false 11 | -------------------------------------------------------------------------------- /tests/integration/match/empty.ai: -------------------------------------------------------------------------------- 1 | let x = match "empty" { 2 | }; // Error at '}': Empty match not allowed. A match expression must have at least one arm. 3 | -------------------------------------------------------------------------------- /tests/integration/match/enum.ai: -------------------------------------------------------------------------------- 1 | enum Color { 2 | Red = "red", 3 | Green = "green", 4 | Blue = "blue", 5 | } 6 | 7 | fn match_color(color) { 8 | return match color { 9 | Color::Red => "found red", 10 | Color::Green => "found green", 11 | Color::Blue => { 12 | print("processing blue"); 13 | "found blue" 14 | }, 15 | }; 16 | } 17 | 18 | let red = Color::Red; 19 | let green = Color::Green; 20 | let blue = Color::Blue; 21 | 22 | print(match_color(red)); // expect: found red 23 | print(match_color(green)); // expect: found green 24 | print(match_color(blue)); 25 | // expect: processing blue 26 | // expect: found blue 27 | -------------------------------------------------------------------------------- /tests/integration/match/error.ai: -------------------------------------------------------------------------------- 1 | enum FileError! { 2 | NotFound = "file not found", 3 | PermissionDenied = "permission denied", 4 | } 5 | 6 | fn match_error(err) { 7 | return match err { 8 | FileError!::NotFound => "404", 9 | FileError!::PermissionDenied => { 10 | print("access denied"); 11 | "403" 12 | }, 13 | _ => "unknown error", 14 | }; 15 | } 16 | 17 | let err1 = FileError!::NotFound; 18 | let err2 = FileError!::PermissionDenied; 19 | 20 | print(match_error(err1)); // expect: 404 21 | print(match_error(err2)); 22 | // expect: access denied 23 | // expect: 403 24 | -------------------------------------------------------------------------------- /tests/integration/match/global_scope.ai: -------------------------------------------------------------------------------- 1 | match "@email" { 2 | "bye" => print("farewell"), 3 | x if x.starts_with("@") => print("at"), // expect: at 4 | _ => print("unknown"), 5 | }; 6 | 7 | let x = match "xyz" { 8 | "bye" => "farewell", 9 | x if x.starts_with("@") => "at", 10 | _ => "unknown", 11 | }; 12 | print(x); // expect: unknown 13 | -------------------------------------------------------------------------------- /tests/integration/match/mutiple_patterns.ai: -------------------------------------------------------------------------------- 1 | fn match_string(s) { 2 | return match s { 3 | "hello" | "hi" => "greeting", 4 | "bye" => "farewell", 5 | _ => "other", 6 | }; 7 | } 8 | print(match_string("hi")); // expect: greeting 9 | print(match_string("hello")); // expect: greeting 10 | print(match_string("bye")); // expect: farewell 11 | print(match_string("h")); // expect: other 12 | print(match_string("abc")); // expect: other 13 | 14 | fn match_number(n) { 15 | return match n { 16 | 1 | 5 | 7 => "lucky", 17 | 42 => "magic", 18 | _ => "funny", 19 | }; 20 | } 21 | print(match_number(0)); // expect: funny 22 | print(match_number(1)); // expect: lucky 23 | print(match_number(5)); // expect: lucky 24 | print(match_number(7)); // expect: lucky 25 | print(match_number(42)); // expect: magic 26 | print(match_number(100)); // expect: funny 27 | -------------------------------------------------------------------------------- /tests/integration/match/number.ai: -------------------------------------------------------------------------------- 1 | fn match_number(n) { 2 | let x = match n { 3 | 0 => "zero", 4 | x if x < 0 and x % 2 == 0 => "negative even", 5 | x if x < 0 => "negative odd", 6 | n if n % 2 == 0 => "positive even", 7 | _ => "positive odd", 8 | }; 9 | x 10 | } 11 | 12 | print(match_number(0)); // expect: zero 13 | print(match_number(-2)); // expect: negative even 14 | print(match_number(-3)); // expect: negative odd 15 | print(match_number(4)); // expect: positive even 16 | print(match_number(5)); // expect: positive odd 17 | -------------------------------------------------------------------------------- /tests/integration/match/range.ai: -------------------------------------------------------------------------------- 1 | fn match_range(n) { 2 | return match n { 3 | ..0 => "negative", 4 | 0 => "zero", 5 | 1..=10 => "small", 6 | 11..=20 if n % 2 == 0 => "even medium", 7 | 11..=20 => "odd medium", 8 | 21..31 => "big medium", 9 | 31.. => { 10 | print("rest arm"); 11 | "large" 12 | }, 13 | }; 14 | } 15 | 16 | print(match_range(-1)); // expect: negative 17 | print(match_range(0)); // expect: zero 18 | print(match_range(1)); // expect: small 19 | print(match_range(10)); // expect: small 20 | print(match_range(12)); // expect: even medium 21 | print(match_range(15)); // expect: odd medium 22 | print(match_range(20)); // expect: even medium 23 | print(match_range(21)); // expect: big medium 24 | print(match_range(30)); // expect: big medium 25 | print(match_range(31)); 26 | // expect: rest arm 27 | // expect: large 28 | print(match_range(42)); 29 | // expect: rest arm 30 | // expect: large 31 | -------------------------------------------------------------------------------- /tests/integration/method/arity.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn method0(self) { return "no args"; } 3 | fn method1(self, a) { return a; } 4 | fn method2(self, a, b) { return a + b; } 5 | fn method3(self, a, b, c) { return a + b + c; } 6 | fn method4(self, a, b, c, d) { return a + b + c + d; } 7 | fn method5(self, a, b, c, d, e) { return a + b + c + d + e; } 8 | fn method6(self, a, b, c, d, e, f) { return a + b + c + d + e + f; } 9 | fn method7(self, a, b, c, d, e, f, g) { return a + b + c + d + e + f + g; } 10 | fn method8(self, a, b, c, d, e, f, g, h) { return a + b + c + d + e + f + g + h; } 11 | } 12 | 13 | let foo = Foo(); 14 | print(foo.method0()); // expect: no args 15 | print(foo.method1(1)); // expect: 1 16 | print(foo.method2(1, 2)); // expect: 3 17 | print(foo.method3(1, 2, 3)); // expect: 6 18 | print(foo.method4(1, 2, 3, 4)); // expect: 10 19 | print(foo.method5(1, 2, 3, 4, 5)); // expect: 15 20 | print(foo.method6(1, 2, 3, 4, 5, 6)); // expect: 21 21 | print(foo.method7(1, 2, 3, 4, 5, 6, 7)); // expect: 28 22 | print(foo.method8(1, 2, 3, 4, 5, 6, 7, 8)); // expect: 36 23 | -------------------------------------------------------------------------------- /tests/integration/method/empty_block.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn bar(self) {} 3 | } 4 | 5 | print(Foo().bar()); // expect: nil 6 | -------------------------------------------------------------------------------- /tests/integration/method/extra_arguments.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn method(self, a, b) { 3 | print(a); 4 | print(b); 5 | } 6 | } 7 | 8 | Foo().method(1, 2, 3, 4); // expect runtime error: Expected 2 arguments but got 4. 9 | -------------------------------------------------------------------------------- /tests/integration/method/missing_arguments.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn method(self, a, b) {} 3 | } 4 | 5 | Foo().method(1); // expect runtime error: Expected 2 arguments but got 1. 6 | -------------------------------------------------------------------------------- /tests/integration/method/not_found.ai: -------------------------------------------------------------------------------- 1 | class Foo {} 2 | 3 | Foo().unknown(); // expect runtime error: Undefined property 'unknown'. 4 | -------------------------------------------------------------------------------- /tests/integration/method/print_bound_method.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn method(self) { } 3 | } 4 | let foo = Foo(); 5 | print(foo.method); // expect: 6 | -------------------------------------------------------------------------------- /tests/integration/method/refer_to_name.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn method(self) { 3 | print(method); // expect runtime error: Undefined variable 'method'. 4 | } 5 | } 6 | 7 | Foo().method(); 8 | -------------------------------------------------------------------------------- /tests/integration/module/invalid_syntax.ai: -------------------------------------------------------------------------------- 1 | use abc; // Could not find module 'abc'. 2 | 3 | pub use abc; // Error at 'use': 'pub' modifier cannot be used with 'use' statement. 4 | 5 | pub; // Error at ';': Expect expression. 6 | -------------------------------------------------------------------------------- /tests/integration/nil/literal.ai: -------------------------------------------------------------------------------- 1 | print(nil); // expect: nil 2 | -------------------------------------------------------------------------------- /tests/integration/number/decimal_point_at_eof.ai: -------------------------------------------------------------------------------- 1 | // [line 2] Error at end: Expect property name after '.'. 2 | 123. -------------------------------------------------------------------------------- /tests/integration/number/leading_dot.ai: -------------------------------------------------------------------------------- 1 | // [line 2] Error at '.': Expect expression. 2 | .123; 3 | -------------------------------------------------------------------------------- /tests/integration/number/literals.ai: -------------------------------------------------------------------------------- 1 | print(123); // expect: 123 2 | print(987654); // expect: 987654 3 | print(0); // expect: 0 4 | print(-0); // expect: -0 5 | 6 | print(123.456); // expect: 123.456 7 | print(-0.001); // expect: -0.001 8 | -------------------------------------------------------------------------------- /tests/integration/number/nan_equality.ai: -------------------------------------------------------------------------------- 1 | let nan = 0/0; 2 | 3 | print(nan == 0); // expect: false 4 | print(nan != 1); // expect: true 5 | 6 | // NaN is not equal to self. 7 | print(nan == nan); // expect: false 8 | print(nan != nan); // expect: true 9 | -------------------------------------------------------------------------------- /tests/integration/number/trailing_dot.ai: -------------------------------------------------------------------------------- 1 | // [line 2] Error at ';': Expect property name after '.'. 2 | 123.; 3 | -------------------------------------------------------------------------------- /tests/integration/object/computed_key.ai: -------------------------------------------------------------------------------- 1 | // Basic computed property 2 | let key = "x"; 3 | let obj = {[key]: 1}; 4 | print(obj.x); // expect: 1 5 | 6 | // Mixed with regular properties 7 | let k = "y"; 8 | let obj2 = { 9 | x: 1, // Regular 10 | [k]: 2, // Computed 11 | "z": 3 // Quoted 12 | }; 13 | print(obj2[k]); // expect: 2 14 | 15 | // Expressions as keys 16 | let prefix = "key"; 17 | fn get_key() { 18 | return prefix; 19 | } 20 | let obj3 = { 21 | [prefix + "1"]: "value", 22 | [get_key()]: "dynamic", 23 | // [1 + 2]: "three" 24 | }; 25 | print(obj3["key1"]); // expect: value 26 | print(obj3["key"]); // expect: dynamic 27 | 28 | // Nested computed properties 29 | let k1 = "outer"; 30 | let k2 = "inner"; 31 | let nested = { 32 | [k1]: { 33 | [k2]: "value" 34 | } 35 | }; 36 | print(nested[k1][k2]); // expect: value -------------------------------------------------------------------------------- /tests/integration/object/equality.ai: -------------------------------------------------------------------------------- 1 | print({} == {}); // expect: false 2 | print({} != {}); // expect: true 3 | print({} == nil); // expect: false 4 | print({} != nil); // expect: true 5 | print({} == {"a": 1}); // expect: false 6 | print({"a": 1} == {"a": 1}); // expect: false 7 | print({"a": 1} != {"a": 1}); // expect: true 8 | 9 | let x = {}; 10 | let y = {}; 11 | print(x == y); // expect: false 12 | 13 | let obj1 = {"a": 1}; 14 | print(obj1 == obj1); // expect: true 15 | obj1["b"] = 2; 16 | print(obj1 == obj1); // expect: true 17 | 18 | let obj2 = {"a": 1}; 19 | print(obj1 == obj2); // expect: false 20 | print(obj1 != obj2); // expect: true 21 | -------------------------------------------------------------------------------- /tests/integration/object/invalid_syntax.ai: -------------------------------------------------------------------------------- 1 | let x1 = { 2 | 1: "value1" // Error at '1': Expect property name string, identifier, or computed [expression]. 3 | }; 4 | let x2= { 5 | 1 // Error at '1': Expect property name string, identifier, or computed [expression]. 6 | }; 7 | let y1 = { 8 | "a" 1 // Error at '1': Expect ':' after property name. 9 | }; 10 | let z = { "z": 1; // Error at ';': Expect '}' after object literal. -------------------------------------------------------------------------------- /tests/integration/object/normal.ai: -------------------------------------------------------------------------------- 1 | let empty = {}; 2 | print(empty); // expect: {} 3 | 4 | const point = { 5 | x: 5 + 5, 6 | y: 100 / 5, 7 | }; 8 | 9 | print(point.x); // expect: 10 10 | print(point.y); // expect: 20 11 | print(point["x"]); // expect: 10 12 | 13 | let key = "x"; 14 | point[key] = 30; 15 | print(point[key]); // expect: 30 16 | print(point["invalid_key"]); // expect: nil 17 | print(point.invalid_key); // expect: nil 18 | print(point.key); // expect: nil 19 | 20 | if point.x > 1 and point["y"] < 100 { 21 | print("Bingo!"); // expect: Bingo! 22 | } 23 | 24 | let nested = { 25 | pos: {x: 1, y: 2, t: "tt"}, 26 | color: "red", 27 | }; 28 | print(nested.pos.x); // expect: 1 29 | print(nested.pos.t); // expect: tt 30 | print(nested.color); // expect: red 31 | 32 | nested.pos.xx = "xx"; 33 | print(nested.pos.xx); // expect: xx 34 | -------------------------------------------------------------------------------- /tests/integration/object/shorthand.ai: -------------------------------------------------------------------------------- 1 | // Basic shorthand 2 | let name = "Alice"; 3 | let age = 30; 4 | let obj = {name, age}; 5 | print(obj.name); // expect: Alice 6 | print(obj.age); // expect: 30 7 | 8 | // Mixed with other property types 9 | let x = 1; 10 | let color = "blue"; 11 | let obj2 = { 12 | x, // Shorthand 13 | y: 2, // Regular property 14 | "z": 3, // Quoted property 15 | color // Shorthand 16 | }; 17 | print(obj2.x); // expect: 1 18 | print(obj2.y); // expect: 2 19 | print(obj2.z); // expect: 3 20 | print(obj2.color); // expect: blue 21 | 22 | // Multiple shorthands 23 | let a = 1; 24 | let b = 2; 25 | let c = 3; 26 | let obj3 = {a, b, c}; // {a: 1, b: 2, c: 3} 27 | print(obj3.a); // expect: 1 28 | print(obj3.b); // expect: 2 29 | print(obj3.c); // expect: 3 -------------------------------------------------------------------------------- /tests/integration/object/string_keys.ai: -------------------------------------------------------------------------------- 1 | let obj = { 2 | "name": "Alice", 3 | age: 20, 4 | "my-key": true, 5 | "space key": 1, 6 | }; 7 | print(obj.name); // expect: Alice 8 | print(obj["name"]); // expect: Alice 9 | print(obj.age); // expect: 20 10 | print(obj["age"]); // expect: 20 11 | print(obj["my-key"]); // expect: true 12 | print(obj["space key"]); // expect: 1 13 | -------------------------------------------------------------------------------- /tests/integration/operator/add.ai: -------------------------------------------------------------------------------- 1 | print(123 + 456); // expect: 579 2 | print("str" + "ing"); // expect: string 3 | -------------------------------------------------------------------------------- /tests/integration/operator/add_bool_nil.ai: -------------------------------------------------------------------------------- 1 | true + nil; // expect runtime error: Operands must be two numbers or two strings. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/add_bool_num.ai: -------------------------------------------------------------------------------- 1 | true + 123; // expect runtime error: Operands must be two numbers or two strings. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/add_bool_string.ai: -------------------------------------------------------------------------------- 1 | true + "s"; // expect runtime error: Operands must be two numbers or two strings. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/add_nil_nil.ai: -------------------------------------------------------------------------------- 1 | nil + nil; // expect runtime error: Operands must be two numbers or two strings. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/add_num_nil.ai: -------------------------------------------------------------------------------- 1 | 1 + nil; // expect runtime error: Operands must be two numbers or two strings. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/add_string_nil.ai: -------------------------------------------------------------------------------- 1 | "s" + nil; // expect runtime error: Operands must be two numbers or two strings. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/comparison.ai: -------------------------------------------------------------------------------- 1 | print(1 < 2); // expect: true 2 | print(2 < 2); // expect: false 3 | print(2 < 1); // expect: false 4 | 5 | print(1 <= 2); // expect: true 6 | print(2 <= 2); // expect: true 7 | print(2 <= 1); // expect: false 8 | 9 | print(1 > 2); // expect: false 10 | print(2 > 2); // expect: false 11 | print(2 > 1); // expect: true 12 | 13 | print(1 >= 2); // expect: false 14 | print(2 >= 2); // expect: true 15 | print(2 >= 1); // expect: true 16 | 17 | // Zero and negative zero compare the same. 18 | print(0 < -0); // expect: false 19 | print(-0 < 0); // expect: false 20 | print(0 > -0); // expect: false 21 | print(-0 > 0); // expect: false 22 | print(0 <= -0); // expect: true 23 | print(-0 <= 0); // expect: true 24 | print(0 >= -0); // expect: true 25 | print(-0 >= 0); // expect: true 26 | -------------------------------------------------------------------------------- /tests/integration/operator/divide.ai: -------------------------------------------------------------------------------- 1 | print(8 / 2); // expect: 4 2 | print(12.34 / 12.34); // expect: 1 3 | -------------------------------------------------------------------------------- /tests/integration/operator/divide_nonnum_num.ai: -------------------------------------------------------------------------------- 1 | "1" / 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/divide_num_nonnum.ai: -------------------------------------------------------------------------------- 1 | 1 / "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/equals.ai: -------------------------------------------------------------------------------- 1 | print(nil == nil); // expect: true 2 | 3 | print(true == true); // expect: true 4 | print(true == false); // expect: false 5 | 6 | print(1 == 1); // expect: true 7 | print(1 == 2); // expect: false 8 | 9 | print("str" == "str"); // expect: true 10 | print("str" == "ing"); // expect: false 11 | 12 | print(nil == false); // expect: false 13 | print(false == 0); // expect: false 14 | print(0 == "0"); // expect: false 15 | -------------------------------------------------------------------------------- /tests/integration/operator/equals_class.ai: -------------------------------------------------------------------------------- 1 | // Bound methods have identity equality. 2 | class Foo {} 3 | class Bar {} 4 | 5 | print(Foo == Foo); // expect: true 6 | print(Foo == Bar); // expect: false 7 | print(Bar == Foo); // expect: false 8 | print(Bar == Bar); // expect: true 9 | 10 | print(Foo == "Foo"); // expect: false 11 | print(Foo == nil); // expect: false 12 | print(Foo == 123); // expect: false 13 | print(Foo == true); // expect: false 14 | -------------------------------------------------------------------------------- /tests/integration/operator/equals_method.ai: -------------------------------------------------------------------------------- 1 | // Bound methods have identity equality. 2 | class Foo { 3 | fn method(self) {} 4 | } 5 | 6 | let foo = Foo(); 7 | let fooMethod = foo.method; 8 | 9 | // Same bound method. 10 | print(fooMethod == fooMethod); // expect: true 11 | 12 | // Different closurizations. 13 | print(foo.method == foo.method); // expect: false 14 | -------------------------------------------------------------------------------- /tests/integration/operator/greater_nonnum_num.ai: -------------------------------------------------------------------------------- 1 | "1" > 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/greater_num_nonnum.ai: -------------------------------------------------------------------------------- 1 | 1 > "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/greater_or_equal_nonnum_num.ai: -------------------------------------------------------------------------------- 1 | "1" >= 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/greater_or_equal_num_nonnum.ai: -------------------------------------------------------------------------------- 1 | 1 >= "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/less_nonnum_num.ai: -------------------------------------------------------------------------------- 1 | "1" < 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/less_num_nonnum.ai: -------------------------------------------------------------------------------- 1 | 1 < "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/less_or_equal_nonnum_num.ai: -------------------------------------------------------------------------------- 1 | "1" <= 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/less_or_equal_num_nonnum.ai: -------------------------------------------------------------------------------- 1 | 1 <= "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/modulo.ai: -------------------------------------------------------------------------------- 1 | print(5 % 2); // expect: 1 2 | print(5 % 2 * 2); // expect: 2 3 | print(5 % 2 / 1); // expect: 1 4 | print(10 % 3 - 1); // expect: 0 5 | print(7 % 4 + 1); // expect: 4 6 | print(2 + 3 % 2); // expect: 3 7 | print(2 + (3 % 2)); // expect: 3 8 | print((2 + 3) % 2); // expect: 1 9 | print(10 % 2); // expect: 0 10 | print(10.0 % 2); // expect: 0 11 | print(10.0 % 2.0); // expect: 0 12 | print(2 % 0); // expect: NaN 13 | -------------------------------------------------------------------------------- /tests/integration/operator/modulo_nonnum_num.ai: -------------------------------------------------------------------------------- 1 | "1" % 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/modulo_num_nonnum.ai: -------------------------------------------------------------------------------- 1 | 1 % "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/multiply.ai: -------------------------------------------------------------------------------- 1 | print(5 * 3); // expect: 15 2 | print(12.34 * 0.3); // expect: 3.702 3 | -------------------------------------------------------------------------------- /tests/integration/operator/multiply_nonnum_num.ai: -------------------------------------------------------------------------------- 1 | "1" * 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/multiply_num_nonnum.ai: -------------------------------------------------------------------------------- 1 | 1 * "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/negate.ai: -------------------------------------------------------------------------------- 1 | print(-(3)); // expect: -3 2 | print(--(3)); // expect: 3 3 | print(---(3)); // expect: -3 4 | -------------------------------------------------------------------------------- /tests/integration/operator/negate_nonnum.ai: -------------------------------------------------------------------------------- 1 | -"s"; // expect runtime error: Operand must be a number. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/not.ai: -------------------------------------------------------------------------------- 1 | print(not true); // expect: false 2 | print(not false); // expect: true 3 | print(not not true); // expect: true 4 | 5 | print(not 123); // expect: false 6 | print(not 0); // expect: false 7 | 8 | print(not nil); // expect: true 9 | 10 | print(not ""); // expect: false 11 | 12 | fn foo() {} 13 | print(not foo); // expect: false 14 | -------------------------------------------------------------------------------- /tests/integration/operator/not_class.ai: -------------------------------------------------------------------------------- 1 | class Bar {} 2 | print(not Bar); // expect: false 3 | print(not Bar()); // expect: false 4 | -------------------------------------------------------------------------------- /tests/integration/operator/not_equals.ai: -------------------------------------------------------------------------------- 1 | print(nil != nil); // expect: false 2 | 3 | print(true != true); // expect: false 4 | print(true != false); // expect: true 5 | 6 | print(1 != 1); // expect: false 7 | print(1 != 2); // expect: true 8 | 9 | print("str" != "str"); // expect: false 10 | print("str" != "ing"); // expect: true 11 | 12 | print(nil != false); // expect: true 13 | print(false != 0); // expect: true 14 | print(0 != "0"); // expect: true 15 | -------------------------------------------------------------------------------- /tests/integration/operator/power.ai: -------------------------------------------------------------------------------- 1 | print(3 ** 3); // expect: 27 2 | print(2 ** 4); // expect: 16 3 | print(5 ** 2); // expect: 25 4 | print(5 ** 0); // expect: 1 5 | print(0 ** 0); // expect: 1 6 | print(0 ** 1); // expect: 0 7 | print(2 ** -1); // expect: 0.5 8 | print(2 ** -0.5); // expect: 0.7071067811865476 9 | print(2 ** -0); // expect: 1 10 | print(-1 ** 2); // expect: 1 11 | print(-0.5 ** 2); // expect: 0.25 12 | print(-0.5 ** 3); // expect: -0.125 -------------------------------------------------------------------------------- /tests/integration/operator/power_nonnum_num.ai: -------------------------------------------------------------------------------- 1 | "1" ** 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/power_num_nonnum.ai: -------------------------------------------------------------------------------- 1 | 1 ** "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/subtract.ai: -------------------------------------------------------------------------------- 1 | print(4 - 3); // expect: 1 2 | print(1.2 - 1.2); // expect: 0 3 | -------------------------------------------------------------------------------- /tests/integration/operator/subtract_nonnum_num.ai: -------------------------------------------------------------------------------- 1 | "1" - 1; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/operator/subtract_num_nonnum.ai: -------------------------------------------------------------------------------- 1 | 1 - "1"; // expect runtime error: Operands must be numbers. 2 | -------------------------------------------------------------------------------- /tests/integration/pipe/expr.ai: -------------------------------------------------------------------------------- 1 | let add_one = |x| x + 1; 2 | let multiply_two = |x| x * 2; 3 | let add_n = |x, n| x + n; 4 | 5 | let v = 1; 6 | print(v |> add_one); // expect: 2 7 | print(v |> add_one + 1); // expect: 3 8 | 9 | print(v |> multiply_two); // expect: 2 10 | print(v |> multiply_two + 1); // expect: 3 11 | print(v |> multiply_two |> multiply_two + 1); // expect: 5 12 | 13 | print(v |> add_one |> multiply_two + 1); // expect: 5 14 | print(v |> multiply_two |> add_one * 2); // expect: 6 15 | 16 | print(v |> add_n(10)); // expect: 11 17 | print(v |> add_n(10) + 1); // expect: 12 18 | print(1 + v |> add_n(10)); // expect: 12 19 | print(1 + (v |> add_n(10))); // expect: 12 20 | -------------------------------------------------------------------------------- /tests/integration/pipe/invalid_arg_count.ai: -------------------------------------------------------------------------------- 1 | fn add_n(x, n) { 2 | return x + n; 3 | } 4 | let arr = [1, 2, 3]; 5 | let result = arr |> add_n; // expect runtime error: Expected 2 arguments but got 1. -------------------------------------------------------------------------------- /tests/integration/pipe/invalid_syntax.ai: -------------------------------------------------------------------------------- 1 | let arr = [1, 2, 3]; 2 | let result = arr |>; // Error at ';': Expect function name after |>. 3 | 4 | let arr = [1, 2, 3]; 5 | let result = arr |> map(|x| x + 1; // Error at ';': Expect ')' after arguments. 6 | 7 | let arr = [1, 2, 3]; 8 | let result = arr |>; // Error at ';': Expect function name after |>. 9 | 10 | let arr = [1, 2, 3]; 11 | let result = arr |> |> map(|x| x + 1); // Error at '|>': Expect function name after |>. 12 | 13 | let arr = [1, 2, 3]; 14 | let result = arr |> 123; // Error at '123': Expect function name after |>. 15 | -------------------------------------------------------------------------------- /tests/integration/pipe/precedence.ai: -------------------------------------------------------------------------------- 1 | fn add_one(x) { 2 | return x + 1; 3 | } 4 | let v = 1; 5 | print(v |> add_one()); // expect: 2 6 | print(2 |> add_one()); // expect: 3 7 | print(5 + 2 |> add_one()); // expect: 8 8 | print((5 + 2) |> add_one()); // expect: 8 9 | print(5 - 2 |> add_one()); // expect: 4 10 | print((5 - 2) |> add_one()); // expect: 4 11 | print(5 * 2 |> add_one()); // expect: 11 12 | print((5 * 2) |> add_one()); // expect: 11 13 | print(5 / 2 |> add_one()); // expect: 3.5 14 | print((5 / 2) |> add_one()); // expect: 3.5 15 | print(5 % 2 |> add_one()); // expect: 2 16 | print((5 % 2) |> add_one()); // expect: 2 17 | print(1 + 2 * 3 / 6 - 1 |> add_one()); // expect: 2 18 | -------------------------------------------------------------------------------- /tests/integration/pipe/single_arg_function.ai: -------------------------------------------------------------------------------- 1 | fn double(x) { 2 | return x * 2; 3 | } 4 | 5 | let v = 42; 6 | print(v |> double); // expect: 84 7 | print(v |> double()); // expect: 84 8 | 9 | print(v |> double |> double); // expect: 168 10 | print(v |> double() |> double); // expect: 168 11 | print(v |> double |> double()); // expect: 168 12 | 13 | let add_one = |x| x + 1; 14 | print(v |> add_one); // expect: 43 15 | -------------------------------------------------------------------------------- /tests/integration/precedence.ai: -------------------------------------------------------------------------------- 1 | // * has higher precedence than +. 2 | print(2 + 3 * 4); // expect: 14 3 | 4 | // * has higher precedence than -. 5 | print(20 - 3 * 4); // expect: 8 6 | 7 | // / has higher precedence than +. 8 | print(2 + 6 / 3); // expect: 4 9 | 10 | // / has higher precedence than -. 11 | print(2 - 6 / 3); // expect: 0 12 | 13 | // < has higher precedence than ==. 14 | print(false == 2 < 1); // expect: true 15 | 16 | // > has higher precedence than ==. 17 | print(false == 1 > 2); // expect: true 18 | 19 | // <= has higher precedence than ==. 20 | print(false == 2 <= 1); // expect: true 21 | 22 | // >= has higher precedence than ==. 23 | print(false == 1 >= 2); // expect: true 24 | 25 | // 1 - 1 is not space-sensitive. 26 | print(1 - 1); // expect: 0 27 | print(1 -1); // expect: 0 28 | print(1- 1); // expect: 0 29 | print(1-1); // expect: 0 30 | 31 | // Using () for grouping. 32 | print((2 * (6 - (2 + 2)))); // expect: 4 33 | -------------------------------------------------------------------------------- /tests/integration/regression/394.ai: -------------------------------------------------------------------------------- 1 | { 2 | class A {} 3 | class B(A) {} 4 | print(B); // expect: B 5 | } 6 | -------------------------------------------------------------------------------- /tests/integration/regression/40.ai: -------------------------------------------------------------------------------- 1 | fn caller(g) { 2 | g(); 3 | // g should be a function, not nil. 4 | print(g == nil); // expect: false 5 | } 6 | 7 | fn callCaller() { 8 | let capturedVar = "before"; 9 | let a = "a"; 10 | 11 | fn f() { 12 | // Commenting the next line out prevents the bug! 13 | capturedVar = "after"; 14 | 15 | // Returning anything also fixes it, even nil: 16 | //return nil; 17 | } 18 | 19 | caller(f); 20 | } 21 | 22 | callCaller(); 23 | -------------------------------------------------------------------------------- /tests/integration/return/after_else.ai: -------------------------------------------------------------------------------- 1 | fn f() { 2 | if false { "no"; } else { return "ok"; } 3 | } 4 | 5 | print(f()); // expect: ok 6 | -------------------------------------------------------------------------------- /tests/integration/return/after_if.ai: -------------------------------------------------------------------------------- 1 | fn f() { 2 | if true { return "ok"; } 3 | } 4 | 5 | print(f()); // expect: ok 6 | -------------------------------------------------------------------------------- /tests/integration/return/after_while.ai: -------------------------------------------------------------------------------- 1 | fn f() { 2 | while true { 3 | return "ok"; 4 | } 5 | } 6 | 7 | print(f()); // expect: ok 8 | -------------------------------------------------------------------------------- /tests/integration/return/in_function.ai: -------------------------------------------------------------------------------- 1 | fn f() { 2 | return "ok"; 3 | print("bad"); 4 | } 5 | 6 | print(f()); // expect: ok 7 | -------------------------------------------------------------------------------- /tests/integration/return/in_method.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn method(self) { 3 | return "ok"; 4 | print("bad"); 5 | } 6 | } 7 | 8 | print(Foo().method()); // expect: ok 9 | -------------------------------------------------------------------------------- /tests/integration/return/return_nil_if_no_value.ai: -------------------------------------------------------------------------------- 1 | fn f() { 2 | return; 3 | print("bad"); 4 | } 5 | 6 | print(f()); // expect: nil 7 | -------------------------------------------------------------------------------- /tests/integration/self/closure.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn getClosure(self) { 3 | fn closure() { 4 | return self.toString(); 5 | } 6 | return closure; 7 | } 8 | 9 | fn toString(self) { return "Foo"; } 10 | } 11 | 12 | let closure = Foo().getClosure(); 13 | print(closure()); // expect: Foo 14 | -------------------------------------------------------------------------------- /tests/integration/self/constructor.ai: -------------------------------------------------------------------------------- 1 | class A { 2 | fn new(self) { // Error at 'self': No need to declare 'self' parameter for class constructor. 3 | self.v = 1; 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/integration/self/invalid_self.ai: -------------------------------------------------------------------------------- 1 | class A { 2 | fn f(self="a", a) { // Error at '=': Expect parameter name. 3 | } 4 | } 5 | 6 | class B { 7 | fn f(self: A, a) { // Error at ':': Expect parameter name. 8 | } 9 | } 10 | 11 | class C { 12 | fn f(self a) { // Error at 'a': Expect ',' between 'self' and parameter. 13 | } 14 | } -------------------------------------------------------------------------------- /tests/integration/self/multiple_self.ai: -------------------------------------------------------------------------------- 1 | class A { 2 | fn f(self, self) { // Error at 'self': 'self' only allow as the first paramater. 3 | } 4 | } 5 | 6 | class B { 7 | fn f(a, self) { // Error at 'self': 'self' only allow as the first paramater. 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /tests/integration/self/nested_class.ai: -------------------------------------------------------------------------------- 1 | class Outer { 2 | fn method(self) { 3 | print(self); // expect: Outer {} 4 | 5 | fn f() { 6 | print(self); // expect: Outer {} 7 | 8 | class Inner { 9 | fn method(self) { 10 | print(self); // expect: Inner {} 11 | } 12 | } 13 | 14 | Inner().method(); 15 | } 16 | f(); 17 | } 18 | } 19 | 20 | Outer().method(); 21 | -------------------------------------------------------------------------------- /tests/integration/self/nested_closure.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn getClosure(self) { 3 | fn f() { 4 | fn g() { 5 | fn h() { 6 | return self.toString(); 7 | } 8 | return h; 9 | } 10 | return g; 11 | } 12 | return f; 13 | } 14 | 15 | fn toString(self) { return "Foo"; } 16 | } 17 | 18 | let closure = Foo().getClosure(); 19 | print(closure()()()); // expect: Foo 20 | -------------------------------------------------------------------------------- /tests/integration/self/self_at_top_level.ai: -------------------------------------------------------------------------------- 1 | self; // Error at 'self': Can't use 'self' outside of a class or enum. 2 | -------------------------------------------------------------------------------- /tests/integration/self/self_in_class_method_closure.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn getClosure(self) { 3 | fn closure(self) { // Error at 'self': 'self' parameter is only allowed in class methods 4 | return self.toString(); 5 | } 6 | return closure; 7 | } 8 | } -------------------------------------------------------------------------------- /tests/integration/self/self_in_class_method_lambda.ai: -------------------------------------------------------------------------------- 1 | class A { 2 | fn f(self) { 3 | let labmda = || { 4 | return self.toString(); 5 | }; 6 | return labmda; 7 | } 8 | } 9 | 10 | class B { 11 | fn f() { 12 | let labmda = || { 13 | return self.toString(); // Error at 'self': Can't use 'self' in static method. 14 | }; 15 | return labmda; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/integration/self/self_in_class_static_method.ai: -------------------------------------------------------------------------------- 1 | class A { 2 | fn f() { 3 | print(self.v); // Error at 'self': Can't use 'self' in static method. 4 | } 5 | } 6 | 7 | // self in closure function in static method 8 | class Foo { 9 | fn getClosure() { 10 | fn closure() { 11 | return self.toString(); // Error at 'self': Can't use 'self' in static method. 12 | } 13 | return closure; 14 | } 15 | } -------------------------------------------------------------------------------- /tests/integration/self/self_in_enum_method_closure.ai: -------------------------------------------------------------------------------- 1 | enum Foo { 2 | A, 3 | B, 4 | 5 | fn getClosure(self) { 6 | fn closure(self) { // Error at 'self': 'self' parameter is only allowed in class methods 7 | return self.toString(); 8 | } 9 | return closure; 10 | } 11 | } -------------------------------------------------------------------------------- /tests/integration/self/self_in_enum_method_lambda.ai: -------------------------------------------------------------------------------- 1 | enum A { 2 | fn f(self) { 3 | let labmda = || { 4 | return self.toString(); 5 | }; 6 | return labmda; 7 | } 8 | } 9 | 10 | enum B { 11 | fn f() { 12 | let labmda = || { 13 | return self.toString(); // Error at 'self': Can't use 'self' in static method. 14 | }; 15 | return labmda; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /tests/integration/self/self_in_enum_static_method.ai: -------------------------------------------------------------------------------- 1 | enum A { 2 | fn f() { 3 | print(self.v); // Error at 'self': Can't use 'self' in static method. 4 | } 5 | } 6 | 7 | // self in closure function in static method 8 | enum Foo { 9 | fn getClosure() { 10 | fn closure() { 11 | return self.toString(); // Error at 'self': Can't use 'self' in static method. 12 | } 13 | return closure; 14 | } 15 | } -------------------------------------------------------------------------------- /tests/integration/self/self_in_top_level_function.ai: -------------------------------------------------------------------------------- 1 | fn foo() { 2 | self; // Error at 'self': Can't use 'self' outside of a class or enum. 3 | } 4 | -------------------------------------------------------------------------------- /tests/integration/self/self_outside_of_class.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn bar(self) { return self; } 3 | fn baz(self) { return "baz"; } 4 | } 5 | 6 | print(Foo().bar().baz()); // expect: baz 7 | -------------------------------------------------------------------------------- /tests/integration/static_method/class.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn s() { 3 | print("static"); 4 | } 5 | } 6 | 7 | Foo.s(); // expect: static 8 | Foo.invalid(); // expect runtime error: Undefined static method 'invalid' of class 'Foo'. 9 | -------------------------------------------------------------------------------- /tests/integration/static_method/class_self_call_static_method.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn f(self) { 3 | self.s(); // expect runtime error: 's' is a static method, use static method syntax instead: Foo.s(). 4 | } 5 | 6 | fn s() { 7 | print("static"); 8 | } 9 | } 10 | 11 | Foo().f(); 12 | -------------------------------------------------------------------------------- /tests/integration/static_method/enum.ai: -------------------------------------------------------------------------------- 1 | enum Either { 2 | A = 1, 3 | B = 2, 4 | 5 | fn s() { 6 | print("static"); 7 | } 8 | } 9 | 10 | Either.s(); // expect: static 11 | Either.invalid(); // expect runtime error: Undefined static method 'invalid' of enum 'Either'. 12 | -------------------------------------------------------------------------------- /tests/integration/static_method/enum_self_call_static_method.ai: -------------------------------------------------------------------------------- 1 | enum Either { 2 | A = 1, 3 | B = 2, 4 | 5 | fn f(self) { 6 | self.s(); // expect runtime error: 's' is a static method, use static method syntax instead: Either.s(). 7 | } 8 | 9 | fn s() { 10 | print("static"); 11 | } 12 | } 13 | 14 | Either::A.f(); 15 | -------------------------------------------------------------------------------- /tests/integration/static_method/instance_call_static_method1.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn s() { 3 | print("static"); 4 | } 5 | } 6 | 7 | Foo().s(); // expect runtime error: 's' is a static method, use static method syntax instead: Foo.s(). 8 | -------------------------------------------------------------------------------- /tests/integration/static_method/instance_call_static_method2.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn s() { 3 | print("static"); 4 | } 5 | } 6 | 7 | let foo = Foo(); 8 | foo.s(); // expect runtime error: 's' is a static method, use static method syntax instead: Foo.s(). 9 | -------------------------------------------------------------------------------- /tests/integration/static_method/super_in_static_method.ai: -------------------------------------------------------------------------------- 1 | class Base { 2 | fn hi() { 3 | print("hi"); 4 | } 5 | } 6 | 7 | class Foo(Base) { 8 | fn f() { 9 | super.hi(); // Error at 'super': Can't use 'super' in static method. 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /tests/integration/static_method/variant_call_static_method1.ai: -------------------------------------------------------------------------------- 1 | enum Either { 2 | A = 1, 3 | B = 2, 4 | 5 | fn s() { 6 | print("static"); 7 | } 8 | } 9 | 10 | Either::A.s(); // expect runtime error: 's' is a static method, use static method syntax instead: Either.s(). 11 | -------------------------------------------------------------------------------- /tests/integration/static_method/variant_call_static_method2.ai: -------------------------------------------------------------------------------- 1 | enum Either { 2 | A = 1, 3 | B = 2, 4 | 5 | fn s() { 6 | print("static"); 7 | } 8 | } 9 | 10 | let e = Either::A; 11 | e.s(); // expect runtime error: 's' is a static method, use static method syntax instead: Either.s(). 12 | -------------------------------------------------------------------------------- /tests/integration/stdlib/auth/jwt.ai: -------------------------------------------------------------------------------- 1 | use std.auth.jwt; 2 | 3 | // Create payload with claims 4 | let payload = { 5 | sub: "user123", 6 | name: "John Doe", 7 | role: "admin" 8 | }; 9 | 10 | // Create access token that expires in 1 hour (3600 seconds) 11 | let token = jwt.create_access_token(payload, 3600, "your-secret"); 12 | 13 | // Or with custom algorithm 14 | let token = jwt.create_access_token(payload, 3600, "your-secret", jwt.HS256); 15 | 16 | // Decode and verify the token 17 | let decoded = jwt.decode(token, "your-secret", jwt.HS256); 18 | print(decoded.name); // expect: "John Doe" -------------------------------------------------------------------------------- /tests/integration/string/error_after_multiline.ai: -------------------------------------------------------------------------------- 1 | // Tests that we correctly track the line info across multiline strings. 2 | let a = "1 3 | 2 4 | 3 5 | "; 6 | 7 | err; // // expect runtime error: Undefined variable 'err'. -------------------------------------------------------------------------------- /tests/integration/string/escape.ai: -------------------------------------------------------------------------------- 1 | // Basic string literals 2 | print("hello"); // expect: hello 3 | print("hello\nworld"); // expect: hello 4 | // expect: world 5 | print("hello\\world"); // expect: hello\world 6 | 7 | // Escape sequences 8 | print("\\"); // expect: \ 9 | print("\n"); 10 | // expect: "" 11 | // expect: "" 12 | print("\t"); // expect: "\t" 13 | print("\""); // expect: """ 14 | print("\'"); // expect: "'" 15 | 16 | // Mixed content 17 | print("Line 1\nLine 2"); // expect: Line 1 18 | // expect: Line 2 19 | print("Tab\there"); // expect: "Tab\there" 20 | print("Backslash\\here"); // expect: "Backslash\\here" 21 | print("Quote\"here"); // expect: Quote"here 22 | -------------------------------------------------------------------------------- /tests/integration/string/literals.ai: -------------------------------------------------------------------------------- 1 | print("(" + "" + ")"); // expect: () 2 | print("a string"); // expect: a string 3 | 4 | // Non-ASCII. 5 | print("A~¶Þॐஃ"); // expect: A~¶Þॐஃ 6 | -------------------------------------------------------------------------------- /tests/integration/string/multiline.ai: -------------------------------------------------------------------------------- 1 | let a = "1 2 | 2 3 | 3"; 4 | print(a); 5 | // expect: 1 6 | // expect: 2 7 | // expect: 3 8 | -------------------------------------------------------------------------------- /tests/integration/string/raw_string.ai: -------------------------------------------------------------------------------- 1 | // Basic string literals 2 | print(r"hello"); // expect: hello 3 | print(r"hello\nworld"); // expect: hello\nworld 4 | print(r"hello\\world"); // expect: hello\\world 5 | 6 | // Escape sequences 7 | print(r"\\"); // expect: r"\\" 8 | print(r"\n"); // expect: r"\n" 9 | print(r"\t"); // expect: r"\t" 10 | print(r"\""); // expect: r"\"" 11 | print(r"\'"); // expect: r"\'" 12 | 13 | // Mixed content 14 | print(r"Line 1\nLine 2"); // expect: Line 1\nLine 2 15 | print(r"Tab\there"); // expect: r"Tab\there" 16 | print(r"Backslash\\here"); // expect: r"Backslash\\here" 17 | print(r"Quote\"here"); // expect: r"Quote\"here" 18 | -------------------------------------------------------------------------------- /tests/integration/string/unterminated.ai: -------------------------------------------------------------------------------- 1 | // [line 2] Error: Unterminated string. 2 | "this string has no close quote -------------------------------------------------------------------------------- /tests/integration/super/bound_method.ai: -------------------------------------------------------------------------------- 1 | class A { 2 | fn method(self, arg) { 3 | print("A.method(" + arg + ")"); 4 | } 5 | } 6 | 7 | class B(A) { 8 | fn getClosure(self) { 9 | return super.method; 10 | } 11 | 12 | fn method(self, arg) { 13 | print("B.method(" + arg + ")"); 14 | } 15 | } 16 | 17 | 18 | let closure = B().getClosure(); 19 | closure("arg"); // expect: A.method(arg) 20 | -------------------------------------------------------------------------------- /tests/integration/super/call_other_method.ai: -------------------------------------------------------------------------------- 1 | class Base { 2 | fn foo(self) { 3 | print("Base.foo()"); 4 | } 5 | } 6 | 7 | class Derived(Base) { 8 | fn bar(self) { 9 | print("Derived.bar()"); 10 | super.foo(); 11 | } 12 | } 13 | 14 | Derived().bar(); 15 | // expect: Derived.bar() 16 | // expect: Base.foo() 17 | -------------------------------------------------------------------------------- /tests/integration/super/call_same_method.ai: -------------------------------------------------------------------------------- 1 | class Base { 2 | fn foo(self) { 3 | print("Base.foo()"); 4 | } 5 | } 6 | 7 | class Derived(Base) { 8 | fn foo(self) { 9 | print("Derived.foo()"); 10 | super.foo(); 11 | } 12 | } 13 | 14 | Derived().foo(); 15 | // expect: Derived.foo() 16 | // expect: Base.foo() 17 | -------------------------------------------------------------------------------- /tests/integration/super/closure.ai: -------------------------------------------------------------------------------- 1 | class Base { 2 | fn toString(self) { return "Base"; } 3 | } 4 | 5 | class Derived(Base) { 6 | fn getClosure(self) { 7 | fn closure() { 8 | return super.toString(); 9 | } 10 | return closure; 11 | } 12 | 13 | fn toString(self) { return "Derived"; } 14 | } 15 | 16 | let closure = Derived().getClosure(); 17 | print(closure()); // expect: Base 18 | -------------------------------------------------------------------------------- /tests/integration/super/constructor.ai: -------------------------------------------------------------------------------- 1 | class Base { 2 | fn new(a, b) { 3 | print("Base.new(" + a + ", " + b + ")"); 4 | } 5 | } 6 | 7 | class Derived(Base) { 8 | fn new() { 9 | print("Derived.new()"); 10 | super.new("a", "b"); 11 | } 12 | } 13 | 14 | Derived(); 15 | // expect: Derived.new() 16 | // expect: Base.new(a, b) 17 | -------------------------------------------------------------------------------- /tests/integration/super/extra_arguments.ai: -------------------------------------------------------------------------------- 1 | class Base { 2 | fn foo(self, a, b) { 3 | print("Base.foo(" + a + ", " + b + ")"); 4 | } 5 | } 6 | 7 | class Derived(Base) { 8 | fn foo(self) { 9 | print("Derived.foo()"); // expect: Derived.foo() 10 | super.foo("a", "b", "c", "d"); // expect runtime error: Expected 2 arguments but got 4. 11 | } 12 | } 13 | 14 | Derived().foo(); 15 | -------------------------------------------------------------------------------- /tests/integration/super/indirectly_inherited.ai: -------------------------------------------------------------------------------- 1 | class A { 2 | fn foo(self) { 3 | print("A.foo()"); 4 | } 5 | } 6 | 7 | class B(A) {} 8 | 9 | class C(B) { 10 | fn foo(self) { 11 | print("C.foo()"); 12 | super.foo(); 13 | } 14 | } 15 | 16 | C().foo(); 17 | // expect: C.foo() 18 | // expect: A.foo() 19 | -------------------------------------------------------------------------------- /tests/integration/super/missing_arguments.ai: -------------------------------------------------------------------------------- 1 | class Base { 2 | fn foo(self, a, b) { 3 | print("Base.foo(" + a + ", " + b + ")"); 4 | } 5 | } 6 | 7 | class Derived(Base) { 8 | fn foo(self) { 9 | super.foo(1); // expect runtime error: Expected 2 arguments but got 1. 10 | } 11 | } 12 | 13 | Derived().foo(); 14 | -------------------------------------------------------------------------------- /tests/integration/super/no_superclass_bind.ai: -------------------------------------------------------------------------------- 1 | class Base { 2 | fn foo() { 3 | super.doesNotExist; // Error at 'super': Can't use 'super' in a class with no superclass. 4 | } 5 | } 6 | 7 | Base().foo(); 8 | -------------------------------------------------------------------------------- /tests/integration/super/no_superclass_call.ai: -------------------------------------------------------------------------------- 1 | class Base { 2 | fn foo() { 3 | super.doesNotExist(1); // Error at 'super': Can't use 'super' in a class with no superclass. 4 | } 5 | } 6 | 7 | Base().foo(); 8 | -------------------------------------------------------------------------------- /tests/integration/super/no_superclass_method.ai: -------------------------------------------------------------------------------- 1 | class Base {} 2 | 3 | class Derived(Base) { 4 | fn foo(self) { 5 | super.doesNotExist(1); // expect runtime error: Undefined property 'doesNotExist'. 6 | } 7 | } 8 | 9 | Derived().foo(); 10 | -------------------------------------------------------------------------------- /tests/integration/super/parenthesized.ai: -------------------------------------------------------------------------------- 1 | class A { 2 | fn method(self) {} 3 | } 4 | 5 | class B(A) { 6 | fn method(self) { 7 | // [line 8] Error at ')': Expect '.' after 'super'. 8 | (super).method(); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /tests/integration/super/reassign_superclass.ai: -------------------------------------------------------------------------------- 1 | class Base { 2 | fn method(self) { 3 | print("Base.method()"); 4 | } 5 | } 6 | 7 | class Derived(Base) { 8 | fn method(self) { 9 | super.method(); 10 | } 11 | } 12 | 13 | class OtherBase { 14 | fn method(self) { 15 | print("OtherBase.method()"); 16 | } 17 | } 18 | 19 | let derived = Derived(); 20 | derived.method(); // expect: Base.method() 21 | Base = OtherBase; 22 | derived.method(); // expect: Base.method() 23 | -------------------------------------------------------------------------------- /tests/integration/super/self_in_superclass_method.ai: -------------------------------------------------------------------------------- 1 | class Base { 2 | fn new(a) { 3 | self.a = a; 4 | } 5 | } 6 | 7 | class Derived(Base) { 8 | fn new(a, b) { 9 | super.new(a); 10 | self.b = b; 11 | } 12 | } 13 | 14 | let derived = Derived("a", "b"); 15 | print(derived.a); // expect: a 16 | print(derived.b); // expect: b 17 | -------------------------------------------------------------------------------- /tests/integration/super/super_as_parameter.ai: -------------------------------------------------------------------------------- 1 | class Foo { 2 | fn f(super) { // Error at 'super': Can't use 'super' as function paramter. 3 | } 4 | } 5 | 6 | class Bar { 7 | fn f(self, super) { // Error at 'super': Can't use 'super' as function paramter. 8 | } 9 | } -------------------------------------------------------------------------------- /tests/integration/super/super_at_top_level.ai: -------------------------------------------------------------------------------- 1 | super.foo("bar"); // Error at 'super': Can't use 'super' outside of a class. 2 | super.foo; // Error at 'super': Can't use 'super' outside of a class. -------------------------------------------------------------------------------- /tests/integration/super/super_in_closure_in_inherited_method.ai: -------------------------------------------------------------------------------- 1 | class A { 2 | fn say(self) { 3 | print("A"); 4 | } 5 | } 6 | 7 | class B(A) { 8 | fn getClosure(self) { 9 | fn closure() { 10 | super.say(); 11 | } 12 | return closure; 13 | } 14 | 15 | fn say(self) { 16 | print("B"); 17 | } 18 | } 19 | 20 | class C(B) { 21 | fn say(self) { 22 | print("C"); 23 | } 24 | } 25 | 26 | C().getClosure()(); // expect: A 27 | -------------------------------------------------------------------------------- /tests/integration/super/super_in_enum.ai: -------------------------------------------------------------------------------- 1 | enum Foo { 2 | fn f(self) { 3 | super.hi(); // Error at 'super': Can't use 'super' in an enum. 4 | } 5 | } 6 | -------------------------------------------------------------------------------- /tests/integration/super/super_in_inherited_method.ai: -------------------------------------------------------------------------------- 1 | class A { 2 | fn say(self) { 3 | print("A"); 4 | } 5 | } 6 | 7 | class B(A) { 8 | fn test(self) { 9 | super.say(); 10 | } 11 | 12 | fn say(self) { 13 | print("B"); 14 | } 15 | } 16 | 17 | class C(B) { 18 | fn say(self) { 19 | print("C"); 20 | } 21 | } 22 | 23 | C().test(); // expect: A 24 | -------------------------------------------------------------------------------- /tests/integration/super/super_in_top_level_function.ai: -------------------------------------------------------------------------------- 1 | super.bar(); // Error at 'super': Can't use 'super' outside of a class. 2 | fn foo() { 3 | } -------------------------------------------------------------------------------- /tests/integration/super/super_without_dot.ai: -------------------------------------------------------------------------------- 1 | class A {} 2 | 3 | class B(A) { 4 | fn method(self) { 5 | // [line 6] Error at ';': Expect '.' after 'super'. 6 | super; 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /tests/integration/super/super_without_name.ai: -------------------------------------------------------------------------------- 1 | class A {} 2 | 3 | class B(A) { 4 | fn method(self) { 5 | super.; // Error at ';': Expect superclass method name. 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /tests/integration/tuple/index.ai: -------------------------------------------------------------------------------- 1 | let a = (1, 2, 3); 2 | print(a[0]); // expect: 1 3 | print(a[1]); // expect: 2 4 | print(a[2]); // expect: 3 5 | print(a[3]); // expect: nil 6 | -------------------------------------------------------------------------------- /tests/integration/tuple/nested.ai: -------------------------------------------------------------------------------- 1 | let a = (1, 2, 3); 2 | let b = (a, [4], 5); 3 | let c = (a, b, 100); 4 | 5 | print(a); // expect: (1, 2, 3) 6 | print(b); // expect: ((1, 2, 3), [4], 5) 7 | print(c); // expect: ((1, 2, 3), ((1, 2, 3), [4], 5), 100) 8 | 9 | print(a == (1, 2, 3)); // expect: true 10 | print(a == b[0]); // expect: true 11 | print(a == c[1][0]); // expect: true 12 | print(b[0] == c[1][0]); // expect: true 13 | -------------------------------------------------------------------------------- /tests/integration/tuple/syntax.ai: -------------------------------------------------------------------------------- 1 | let empty = (); 2 | print(empty); // expect: () 3 | print(() == ()); // expect: true 4 | print(() == []); // expect: false 5 | 6 | let x = (1); 7 | print(x); // expect: 1 8 | 9 | let one = (1,); 10 | print(one); // expect: (1,) 11 | print(one == 1); // expect: false 12 | 13 | let a = (1, 2); 14 | print(a); // expect: (1, 2) 15 | print(a == a); // expect: true 16 | let b = (1, 2,); 17 | print(b); // expect: (1, 2) 18 | print(a == b); // expect: true 19 | 20 | print([1,2] == (1,2)); // expect: false 21 | 22 | let a = "a"; 23 | let b = "b"; 24 | let c = "c"; 25 | print((a, b, c)); // expect: (a, b, c) 26 | -------------------------------------------------------------------------------- /tests/integration/unexpected_character.ai: -------------------------------------------------------------------------------- 1 | // [line 3] Error: Unexpected character. 2 | // [java line 3] Error at 'b': Expect ')' after arguments. 3 | foo(a | b); 4 | -------------------------------------------------------------------------------- /tests/integration/variable/collide_with_parameter.ai: -------------------------------------------------------------------------------- 1 | fn foo(a) { 2 | let a; // Error at 'a': Already a variable with this name in this scope. 3 | } 4 | -------------------------------------------------------------------------------- /tests/integration/variable/duplicate_local.ai: -------------------------------------------------------------------------------- 1 | { 2 | let a = "value"; 3 | let a = "other"; // Error at 'a': Already a variable with this name in this scope. 4 | } 5 | -------------------------------------------------------------------------------- /tests/integration/variable/duplicate_parameter.ai: -------------------------------------------------------------------------------- 1 | fn foo(arg, 2 | arg) { // Error at 'arg': Already a variable with this name in this scope. 3 | "body"; 4 | } 5 | -------------------------------------------------------------------------------- /tests/integration/variable/early_bound.ai: -------------------------------------------------------------------------------- 1 | let a = "outer"; 2 | { 3 | fn foo() { 4 | print(a); 5 | } 6 | 7 | foo(); // expect: outer 8 | let a = "inner"; 9 | foo(); // expect: outer 10 | } 11 | -------------------------------------------------------------------------------- /tests/integration/variable/in_middle_of_block.ai: -------------------------------------------------------------------------------- 1 | { 2 | let a = "a"; 3 | print(a); // expect: a 4 | let b = a + " b"; 5 | print(b); // expect: a b 6 | let c = a + " c"; 7 | print(c); // expect: a c 8 | let d = b + " d"; 9 | print(d); // expect: a b d 10 | } 11 | -------------------------------------------------------------------------------- /tests/integration/variable/in_nested_block.ai: -------------------------------------------------------------------------------- 1 | { 2 | let a = "outer"; 3 | { 4 | print(a); // expect: outer 5 | } 6 | } -------------------------------------------------------------------------------- /tests/integration/variable/local_from_method.ai: -------------------------------------------------------------------------------- 1 | let foo = "variable"; 2 | 3 | class Foo { 4 | fn method(self) { 5 | print(foo); 6 | } 7 | } 8 | 9 | Foo().method(); // expect: variable 10 | -------------------------------------------------------------------------------- /tests/integration/variable/redeclare_global.ai: -------------------------------------------------------------------------------- 1 | let a = "1"; 2 | let a; 3 | print(a); // expect: nil 4 | -------------------------------------------------------------------------------- /tests/integration/variable/redefine_global.ai: -------------------------------------------------------------------------------- 1 | let a = "1"; 2 | let a = "2"; 3 | print(a); // expect: 2 4 | -------------------------------------------------------------------------------- /tests/integration/variable/scope_reuse_in_different_blocks.ai: -------------------------------------------------------------------------------- 1 | { 2 | let a = "first"; 3 | print(a); // expect: first 4 | } 5 | 6 | { 7 | let a = "second"; 8 | print(a); // expect: second 9 | } 10 | -------------------------------------------------------------------------------- /tests/integration/variable/shadow_and_local.ai: -------------------------------------------------------------------------------- 1 | { 2 | let a = "outer"; 3 | { 4 | print(a); // expect: outer 5 | let a = "inner"; 6 | print(a); // expect: inner 7 | } 8 | } -------------------------------------------------------------------------------- /tests/integration/variable/shadow_global.ai: -------------------------------------------------------------------------------- 1 | let a = "global"; 2 | { 3 | let a = "shadow"; 4 | print(a); // expect: shadow 5 | } 6 | print(a); // expect: global 7 | -------------------------------------------------------------------------------- /tests/integration/variable/shadow_local.ai: -------------------------------------------------------------------------------- 1 | { 2 | let a = "local"; 3 | { 4 | let a = "shadow"; 5 | print(a); // expect: shadow 6 | } 7 | print(a); // expect: local 8 | } 9 | -------------------------------------------------------------------------------- /tests/integration/variable/undefined_global.ai: -------------------------------------------------------------------------------- 1 | print(notDefined); // expect runtime error: Undefined variable 'notDefined'. 2 | -------------------------------------------------------------------------------- /tests/integration/variable/undefined_local.ai: -------------------------------------------------------------------------------- 1 | { 2 | print(notDefined); // expect runtime error: Undefined variable 'notDefined'. 3 | } 4 | -------------------------------------------------------------------------------- /tests/integration/variable/uninitialized.ai: -------------------------------------------------------------------------------- 1 | let a; 2 | print(a); // expect: nil 3 | -------------------------------------------------------------------------------- /tests/integration/variable/unreached_undefined.ai: -------------------------------------------------------------------------------- 1 | if false { 2 | print(notDefined); 3 | } 4 | 5 | print("ok"); // expect: ok 6 | -------------------------------------------------------------------------------- /tests/integration/variable/use_false_as_var.ai: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'false': Expect variable name. 2 | let false = "value"; 3 | -------------------------------------------------------------------------------- /tests/integration/variable/use_global_in_initializer.ai: -------------------------------------------------------------------------------- 1 | let a = "value"; 2 | let a = a; 3 | print(a); // expect: value 4 | -------------------------------------------------------------------------------- /tests/integration/variable/use_local_in_initializer.ai: -------------------------------------------------------------------------------- 1 | let a = "outer"; 2 | { 3 | let a = a; // Error at 'a': Can't read local variable in its own initializer. 4 | } 5 | -------------------------------------------------------------------------------- /tests/integration/variable/use_nil_as_var.ai: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'nil': Expect variable name. 2 | let nil = "value"; 3 | -------------------------------------------------------------------------------- /tests/integration/variable/use_self_as_var.ai: -------------------------------------------------------------------------------- 1 | // [line 2] Error at 'self': Expect variable name. 2 | let self = "value"; 3 | -------------------------------------------------------------------------------- /tests/integration/while/closure_in_body.ai: -------------------------------------------------------------------------------- 1 | let f1; 2 | let f2; 3 | let f3; 4 | 5 | let i = 1; 6 | while i < 4 { 7 | let j = i; 8 | fn f() { print(j); } 9 | 10 | if j == 1 { f1 = f; } 11 | else if j == 2 { f2 = f; } 12 | else { f3 = f; } 13 | 14 | i = i + 1; 15 | } 16 | 17 | f1(); // expect: 1 18 | f2(); // expect: 2 19 | f3(); // expect: 3 20 | -------------------------------------------------------------------------------- /tests/integration/while/missing_close_paren.ai: -------------------------------------------------------------------------------- 1 | while true { print("block"); // [line 2] Error at end: Expect '}' after block. 2 | -------------------------------------------------------------------------------- /tests/integration/while/missing_open_paren.ai: -------------------------------------------------------------------------------- 1 | while true print("block"); } // Error at 'print': Expect '{' before loop body. -------------------------------------------------------------------------------- /tests/integration/while/object_literal_in_condition.ai: -------------------------------------------------------------------------------- 1 | while len({}) > 0 { // Error at '{': Cannot use object literals in flow control conditions 2 | // ... 3 | } -------------------------------------------------------------------------------- /tests/integration/while/return_closure.ai: -------------------------------------------------------------------------------- 1 | fn f() { 2 | while true { 3 | let i = "i"; 4 | fn g() { print(i); } 5 | return g; 6 | } 7 | } 8 | 9 | let h = f(); 10 | h(); // expect: i 11 | -------------------------------------------------------------------------------- /tests/integration/while/return_inside.ai: -------------------------------------------------------------------------------- 1 | fn f() { 2 | while true { 3 | let i = "i"; 4 | return i; 5 | } 6 | } 7 | 8 | print(f()); 9 | // expect: i 10 | -------------------------------------------------------------------------------- /tests/integration/while/syntax.ai: -------------------------------------------------------------------------------- 1 | // Single-expression body. 2 | let c = 0; 3 | while c < 3 { 4 | c += 1; 5 | print(c); 6 | } 7 | // expect: 1 8 | // expect: 2 9 | // expect: 3 10 | 11 | // Block body. 12 | let a = 0; 13 | while a < 3 { 14 | print(a); 15 | a = a + 1; 16 | } 17 | // expect: 0 18 | // expect: 1 19 | // expect: 2 20 | 21 | // Statement bodies. 22 | while false { 23 | if true { 24 | 1; 25 | } else { 26 | 2; 27 | } 28 | } 29 | while false { 30 | while true { 31 | 1; 32 | } 33 | } 34 | while false { 35 | for ;; { 36 | 1; 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /xtask/Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "xtask" 3 | version = "0.1.0" 4 | edition.workspace = true 5 | repository.workspace = true 6 | license.workspace = true 7 | publish = false 8 | 9 | [dependencies] 10 | anyhow = "1.0.94" 11 | clap = { version = "4.5", features = ["derive"] } 12 | --------------------------------------------------------------------------------