├── grammar ├── README.md ├── zed │ ├── .gitignore │ ├── languages │ │ └── rcl │ │ │ ├── brackets.scm │ │ │ └── indents.scm │ ├── extension.toml │ └── extension.rcl ├── tree-sitter-rcl │ ├── test │ │ └── corpus │ │ │ └── special.txt │ ├── .gitignore │ ├── package.json │ ├── bindings │ │ └── rust │ │ │ └── build.rs │ ├── queries │ │ ├── highlights.scm │ │ └── highlights_nvim.scm │ └── Cargo.toml └── rcl.vim │ └── ftdetect │ └── rcl.vim ├── golden ├── rcl │ ├── _import.rcl │ ├── dict_empty.test │ ├── list_sum.test │ ├── set_sum.test │ ├── builtin_method.test │ ├── list_map.test │ ├── set_empty_std.test │ ├── set_to_list.test │ ├── typed_let_null.test │ ├── typed_let_set.test │ ├── typed_let_string.test │ ├── import_relative.test │ ├── list_filter.test │ ├── set_filter.test │ ├── string_remove_prefix.test │ ├── string_remove_suffix.test │ ├── builtin_function.test │ ├── function.test │ ├── set_map_dedup.test │ ├── import_workdir_relative.test │ ├── list_to_set_unique.test │ ├── set_empty_comprehension.test │ ├── list_to_set_dedup.test │ ├── list_enumerate.test │ ├── typed_let_dict.test │ ├── set_except.test │ ├── string_parse_int.test │ ├── set_unpack_runtime_check.test │ ├── list_unpack_runtime_check.test │ ├── dict_keys.test │ ├── dict_values.test │ ├── dict_non_string_keys.test │ ├── import_in_list.test │ ├── dict_unpack.test │ ├── set_empty_annotation.test │ ├── set_floats.test │ ├── set_sort.test │ ├── list_sort.test │ ├── types_meet_null.test │ ├── list_flat_map.test │ ├── int_add.test │ ├── string_to_lowercase.test │ ├── set_unpack.test │ ├── list_unpack.test │ ├── string_to_uppercase.test │ ├── set_flat_map_dedup.test │ ├── set_sort_by.test │ ├── string_split.test │ ├── types_meet_void.test │ ├── types_instance_of.test │ ├── string_split_lines.test │ ├── list_sort_by.test │ ├── typed_let_primitive.test │ ├── typed_subtype_function_ok.test │ ├── list_all.test │ ├── list_any.test │ ├── set_all.test │ ├── set_any.test │ ├── string_parse_number.test │ ├── literals.test │ ├── builtin_function_fmt.test │ ├── dict_identifiers.test │ ├── typed_let_collections.test │ ├── list_key_by.test │ ├── set_key_by.test │ ├── types_subtype_collection_ok.test │ ├── list_group_by.test │ └── set_group_by.test ├── json │ ├── null.test │ ├── negation.test │ ├── list_len.test │ ├── set_len.test │ ├── _import.txt │ ├── double_negation.test │ ├── dict_len.test │ ├── list_reverse.test │ ├── string_replace.test │ ├── empty_dict.test │ ├── list_join.test │ ├── field.test │ ├── function_first_class.test │ ├── list_contains.test │ ├── set_contains.test │ ├── unambiguous_unop.test │ ├── std_read_file_utf8.test │ ├── dict_contains.test │ ├── integer_comparisons.test │ ├── number_exponent.test │ ├── integer_arithmetic.test │ ├── set_to_array.test │ ├── fstring_escapes.test │ ├── std_range.test │ ├── dict_except.test │ ├── conditional.test │ ├── dict_comprehension.test │ ├── list_comprehension_list.test │ ├── list_comprehension_set.test │ ├── set_union.test │ ├── dict_union.test │ ├── string_contains.test │ ├── string_ends_with.test │ ├── boolean_logic.test │ ├── string_starts_with.test │ ├── dict_index.test │ ├── dict_get.test │ ├── function_call.test │ ├── trace.test │ ├── list_index.test │ ├── fstring_triple.test │ ├── fstring_nested.test │ ├── string_chars.test │ ├── number_roundtrip.test │ ├── set_comprehension_empty.test │ ├── string_len.test │ ├── integer_literals.test │ ├── fstring.test │ ├── strings.test │ ├── comparisions.test │ ├── std_format_json.test │ ├── dict_values_strings.test │ ├── function_compare.test │ └── function_capture_environment.test ├── error │ ├── _import_cycle_a.rcl │ ├── _import_cycle_b.rcl │ ├── _import_cycle_c.rcl │ ├── lex_unmatched_rbrace.test │ ├── lex_unmatched_rparen.test │ ├── lex_unmatched_rbracket.test │ ├── runtime_division_by_zero.test │ ├── parse_expected_expression.test │ ├── runtime_unknown_variable.test │ ├── lex_number_empty_0b.test │ ├── lex_number_empty_0x.test │ ├── parse_content_after_main.test │ ├── parse_let_non_ident.test │ ├── parse_let_not_eq.test │ ├── lex_unrecognized_punctuation.test │ ├── parse_bang.test │ ├── lex_number_end_exponent.test │ ├── _import_clean_env.rcl │ ├── lex_number_end_decimal_point.test │ ├── lex_multibyte_escape.test │ ├── parse_seq_condition_no_colon.test │ ├── parse_type_annotation_not_type.test │ ├── lex_number_end_exponent_multibyte.test │ ├── parse_assert_no_colon.test │ ├── parse_seq_invalid_terminator_comma.test │ ├── runtime_index_out_of_bounds.test │ ├── runtime_division_non_integer.test │ ├── runtime_not_fn_2.test │ ├── runtime_overflow_neg.test │ ├── runtime_unpack_assocs_list.test │ ├── runtime_unpack_assocs_set.test │ ├── unescape_ubrace_not_hex.test │ ├── unescape_ubrace_unmatched.test │ ├── typecheck_unpack_elem_not_iterable.test │ ├── lex_unmatched_lbrace_eof.test │ ├── lex_unmatched_lparen_eof.test │ ├── runtime_unpack_assocs_not_iterable.test │ ├── runtime_unpack_elems_not_iterable.test │ ├── typecheck_unpack_assoc_not_iterable.test │ ├── lex_unmatched_lbracket_eof.test │ ├── parse_type_annotation_no_let.test │ ├── runtime_call_arity_dict_get_too_few.test │ ├── runtime_overflow_add.test │ ├── runtime_not_fn_1.test │ ├── runtime_overflow_mul.test │ ├── unescape_invalid.test │ ├── parse_unmatched_lbrace.test │ ├── parse_unmatched_lbracket.test │ ├── parse_unmatched_lparen.test │ ├── typecheck_iter_not_iterable.test │ ├── unescape_ubrace_too_short.test │ ├── lex_non_ascii.test │ ├── lex_unmatched_lbrace.test │ ├── lex_unmatched_lbracket.test │ ├── lex_unmatched_lparen.test │ ├── runtime_overflow_sub.test │ ├── unescape_ubrace_too_long.test │ ├── lex_hash_comment.test │ ├── std_read_file_utf8_arity_too_few.test │ ├── parse_unmatched_lparen_lambda.test │ ├── runtime_import_clean_env.test │ ├── typecheck_unpack_assoc_invalid_list.test │ ├── parse_fstring_double_no_hole.test │ ├── runtime_import_absolute.test │ ├── runtime_import_not_string_literal.test │ ├── std_read_file_utf8_arity_too_many.test │ ├── parse_let_no_semicolon.test │ ├── type_unknown_variable_fstring.test │ ├── unescape_uppercase.test │ ├── lex_ascii_control.test │ ├── parse_integer_overflow_hex.test │ ├── typecheck_collection_list_kv.test │ ├── parse_integer_overflow_dec.test │ ├── runtime_call_arity_dict_get_too_many.test │ ├── runtime_index_dict_key_absent.test │ ├── runtime_unpack_elems_dict.test │ ├── lex_unclosed_string_literal.test │ ├── parse_if_no_else.test │ ├── lex_number_empty_0x_underscores.test │ ├── lex_unclosed_triple_string_literal.test │ ├── parse_call_unmatched.test │ ├── parse_string_newline.test │ ├── unescape_ujson_not_scalar.test │ ├── runtime_call_arity_contains.test │ ├── std_string_parse_number_empty.test │ ├── runtime_assertion_failure.test │ ├── std_string_replace_not_string_needle.test │ ├── std_string_split_empty_separator.test │ ├── unescape_ubrace_not_scalar.test │ ├── runtime_import_fstring.test │ ├── runtime_import_not_found.test │ ├── parse_assert_semicolon.test │ ├── std_list_sum_not_int.test │ ├── std_string_contains_not_string.test │ ├── unescape_u_not_hex.test │ ├── unescape_ujson_too_short.test │ ├── std_string_ends_with_not_string.test │ ├── std_string_replace_not_string_replacement.test │ ├── unescape_linebreak.test │ ├── unescape_ujson_not_hex.test │ ├── std_string_parse_int.test │ ├── std_string_parse_number_invalid_single.test │ ├── std_string_split_not_string.test │ ├── typecheck_unpack_assoc_list.test │ ├── lex_fstring_double_string_unclosed.test │ ├── std_string_parse_number_invalid_padding.test │ ├── std_string_remove_prefix_arg_type.test │ ├── std_string_remove_suffix_arg_type.test │ ├── std_string_starts_with_not_string.test │ ├── lex_fstring_double_hole_unclosed_eof.test │ ├── parse_type_annotation_fn_no_paren.test │ ├── std_range_not_int_0.test │ ├── std_range_not_int_1.test │ ├── typecheck_unpack_elem_invalid.test │ ├── runtime_fstring_not_formattable.test │ ├── typecheck_unpack_elem_dict.test │ ├── runtime_overflow_add_2.test │ ├── typecheck_iter_arity_list.test │ ├── typecheck_iter_arity_set.test │ ├── parse_type_annotation_unmatched_bracket.test │ ├── std_list_any_not_bool.test │ ├── std_string_remove_prefix.test │ ├── std_string_remove_suffix.test │ ├── type_index_inferred_mismatch.test │ ├── parse_type_annotation_fn_fat_arrow.test │ ├── runtime_unknown_field_string.test │ ├── std_read_file_utf8_not_string.test │ ├── lex_fstring_double_hole_unclosed.test │ ├── runtime_assertion_failure_value.test │ ├── typecheck_collection_kv_scalar.test │ ├── typecheck_collection_scalar_kv.test │ ├── parse_invalid_comment.test │ ├── std_list_filter_not_bool.test │ ├── std_list_sum_overflow.test │ ├── type_msg_bool.test │ ├── type_msg_null.test │ ├── std_number_round_negative.test │ ├── type_index_inferred_mismatch_2.test │ ├── type_index_void.test │ ├── typecheck_iter_arity_record.test │ ├── parse_term_needs_paren.test │ ├── std_string_parse_number_invalid_multi.test │ ├── type_let_mismatch_primitive.test │ ├── type_index_fn.test │ ├── std_list_flat_map_not_collection.test │ ├── typecheck_collection_unpack_elem.test │ ├── parse_ambiguous_unop.test │ ├── std_list_to_set_unique.test │ ├── parse_fstring_double_hole_unclosed.test │ ├── typecheck_collection_unpack_assoc.test │ ├── print_long_multibyte.test │ ├── std_range_not_int_1a.test │ ├── type_let_mismatch_in_list.test │ ├── runtime_unknown_field_record.test │ ├── std_number_round_too_large.test │ ├── typecheck_unknown_variable_record_hint.test │ ├── typecheck_unpack_assoc_invalid_list_2.test │ ├── typecheck_unpack_assoc_invalid_set_annotate.test │ ├── std_set_transitive_closure_not_iterable.test │ ├── print_long_suffix.test │ ├── parse_integer_overflow_bin.test │ ├── parse_dict_not_ident.test │ ├── parse_seq_if_ternary.test │ ├── std_number_round_overflow_range_pos.test │ ├── std_read_file_utf8_not_found.test │ ├── type_msg_set.test │ ├── parse_depth.test │ ├── print_long_prefix.test │ ├── runtime_import_sandbox_deny.test │ ├── std_list_fold_not_callable.test │ ├── std_set_transitive_closure_depth_function.test │ ├── typecheck_unpack_assoc_invalid_set_infer.test │ ├── typecheck_unpack_elem_runtime.test │ ├── runtime_assertion_with_newline.test │ ├── runtime_std_range_too_big.test │ ├── typecheck_unpack_assoc_runtime.test │ ├── std_list_group_by_not_callable.test │ ├── std_list_fold_depth_method.test │ ├── std_string_parse_number_overflow.test │ ├── type_in_unpack_element.test │ ├── type_index_dict_fn_key.test │ ├── type_let_function_arity.test │ ├── type_msg_function.test │ ├── std_list_map_error.test │ ├── parse_binop_precedence.test │ ├── runtime_unknown_field_list.test │ ├── runtime_import_cycle.test │ ├── std_list_any_inner_error.test │ ├── std_list_fold_depth_set.test │ ├── type_let_mismatch_long_type.test │ ├── call_stack_context_method.test │ ├── call_stack_context_function.test │ ├── std_set_transitive_closure_err.test │ ├── parse_unop_in_binop.test │ └── type_in_unpack_key.test ├── raw │ ├── set.test │ ├── string.test │ └── list.test ├── types │ ├── expr_list_void.test │ ├── static_union_supertype_ok.test │ ├── runtime_union_subtype_ok.test │ ├── static_union_subtype_union_ok.test │ ├── static_not_fn_1.test │ ├── runtime_union_subtype_defer_ok.test │ ├── expr_unknown_type.test │ ├── runtime_index_list_float.test │ ├── static_union_subtype_fn_ok.test │ ├── static_function_ok.test │ ├── expr_args_unknown_generic.test │ ├── static_name_hint_list.test │ ├── static_name_hint_number.test │ ├── expr_args_dict_arity.test │ ├── runtime_iter_null.test │ ├── static_name_hint_bool.test │ ├── expr_args_set_arity.test │ ├── expr_args_union_arity_0.test │ ├── static_call_arity_lambda1_too_many.test │ ├── expr_args_list_arity.test │ ├── runtime_union_any.test │ ├── static_index_list_not_number.test │ ├── runtime_index_null.test │ ├── static_listcomp_condition_not_bool.test │ ├── static_name_hint_dict.test │ ├── static_number_neg_inner.test │ ├── static_binop_unsupported.test │ ├── static_condition_not_bool.test │ ├── runtime_index_string.test │ ├── static_unop_unsupported.test │ ├── expr_args_union_arity_1.test │ ├── runtime_union_subtype_defer_ok_2.test │ ├── static_call_arity_lambda1_too_few.test │ ├── static_function_defer.test │ ├── static_index_null.test │ ├── static_index_string.test │ ├── expr_args_set_none.test │ ├── expr_args_list_none.test │ ├── static_assertion_not_bool.test │ ├── static_call_arity_lambda2_too_many.test │ ├── static_union_null.test │ ├── expr_args_union_none.test │ ├── expr_args_dict_none.test │ ├── static_call_arity_lambda2_too_few.test │ ├── runtime_index_list_string.test │ ├── static_collection_dict_unexpected.test │ ├── runtime_iter_set.test │ ├── runtime_iter_list.test │ ├── static_collection_set_not_scalar.test │ ├── runtime_iter_dict.test │ ├── runtime_type_assert_not_bool.test │ ├── static_collection_dict_not_kv.test │ ├── runtime_union_subtype_defer_err.test │ ├── meet_builtin.test │ ├── static_collection_dict_not_dict.test │ ├── static_union_subtype_err.test │ ├── mismatch_short_long.test │ ├── runtime_function_arity.test │ ├── runtime_type_annotation_mismatch.test │ ├── runtime_function_arg_defer.test │ ├── static_union_supertype_err.test │ ├── static_collection_set_inner.test │ ├── static_collection_list_inner.test │ ├── runtime_dict_key.test │ ├── runtime_dict_value.test │ ├── static_collection_dict_key.test │ └── static_collection_dict_value.test ├── cmd │ ├── query_basic.test │ ├── patch_basic.test │ ├── _input_fmt_err.rcl │ ├── patch_check_unchanged.test │ ├── format_check_unchanged.test │ ├── format_check_cli_unchanged.test │ ├── format_check_changed.test │ ├── patch_check_changed.test │ ├── format_check_cli_changed.test │ ├── patch_error_not_found_top_level.test │ ├── patch_error_not_found_collection.test │ ├── patch_error_not_found_statements.test │ ├── patch_error_invalid_replacement.test │ ├── patch_let_expr.test │ ├── patch_error_invalid_path.test │ └── _input_fmt_ok.rcl ├── fmt │ ├── empty_collections.test │ ├── number_literals.test │ ├── drop_leading_blank_lines.test │ ├── unop.test │ ├── collapse_blank_lines.test │ ├── seq_comment.test │ ├── chain_atomic.test │ ├── import.test │ ├── index_tall.test │ ├── json_dict.test │ ├── nested_let.test │ ├── parens_multiline.test │ ├── call_multiline.test │ ├── if_else_flush.test │ ├── seq_complexity.test │ ├── binop.test │ ├── statement_complexity.test │ ├── seq_unpack.test │ ├── types_blank_in_annotation.test │ ├── string_triple_quotes.test │ └── shebang.test ├── error_json │ ├── lambda_function.test │ ├── non_string_key.test │ ├── builtin_method.test │ ├── builtin_function.test │ ├── builtin_function_span.test │ └── std_format_json_builtin_method.test ├── error_raw │ ├── not_string_top_level.test │ ├── not_string_in_set.test │ └── not_string_in_list.test ├── toml │ ├── error_key_not_string.test │ ├── error_function_builtin.test │ ├── error_function_method.test │ ├── error_function_lambda.test │ ├── top_level_set_of_dict.test │ ├── error_not_dict.test │ ├── top_level_dict.test │ ├── error_null.test │ ├── string.test │ ├── top_level_list_of_dict.test │ ├── keys_quoted.test │ └── table_in_table.test ├── build │ ├── error_no_format.test │ ├── error_no_contents.test │ ├── error_width_not_int_1.test │ ├── error_width_not_int_2.test │ ├── error_width_out_of_range.test │ ├── error_banner_invalid.test │ ├── error_unknown_field.test │ ├── build_multiple.test │ ├── error_format_not_string.test │ ├── error_format_invalid.test │ ├── build_toml.test │ ├── build_json.test │ └── build_yaml.test ├── json_lines │ ├── not_list.test │ └── list.test └── yaml_stream │ └── not_list.test ├── website └── .gitignore ├── fuzz ├── .gitignore ├── fuzz_targets │ └── fuzz_cli.rs ├── src │ └── lib.rs ├── dictionary_base.txt └── dictionary_cli.txt ├── .gitmodules ├── pyrcl ├── test.rcl ├── rcl.pyi └── Cargo.toml ├── docs └── pull_request_template.md ├── rust-toolchain.toml ├── .gitignore ├── examples ├── buckets.rcl └── build.rcl └── Cargo.toml /grammar/README.md: -------------------------------------------------------------------------------- 1 | ../docs/grammars.md -------------------------------------------------------------------------------- /grammar/zed/.gitignore: -------------------------------------------------------------------------------- 1 | grammars 2 | -------------------------------------------------------------------------------- /golden/rcl/_import.rcl: -------------------------------------------------------------------------------- 1 | { answer = 42 } 2 | -------------------------------------------------------------------------------- /golden/json/null.test: -------------------------------------------------------------------------------- 1 | null 2 | 3 | # output: 4 | null 5 | -------------------------------------------------------------------------------- /website/.gitignore: -------------------------------------------------------------------------------- 1 | rcl.js 2 | rcl_bg.wasm 3 | *.br 4 | -------------------------------------------------------------------------------- /golden/rcl/dict_empty.test: -------------------------------------------------------------------------------- 1 | {} 2 | 3 | # output: 4 | {} 5 | -------------------------------------------------------------------------------- /fuzz/.gitignore: -------------------------------------------------------------------------------- 1 | target 2 | corpus 3 | artifacts 4 | coverage 5 | -------------------------------------------------------------------------------- /golden/error/_import_cycle_a.rcl: -------------------------------------------------------------------------------- 1 | import "_import_cycle_b.rcl" 2 | -------------------------------------------------------------------------------- /golden/error/_import_cycle_b.rcl: -------------------------------------------------------------------------------- 1 | import "_import_cycle_c.rcl" 2 | -------------------------------------------------------------------------------- /golden/error/_import_cycle_c.rcl: -------------------------------------------------------------------------------- 1 | import "_import_cycle_a.rcl" 2 | -------------------------------------------------------------------------------- /golden/json/negation.test: -------------------------------------------------------------------------------- 1 | not true 2 | 3 | # output: 4 | false 5 | -------------------------------------------------------------------------------- /golden/json/list_len.test: -------------------------------------------------------------------------------- 1 | ["a", "b", "c"].len() 2 | 3 | # output: 4 | 3 5 | -------------------------------------------------------------------------------- /golden/json/set_len.test: -------------------------------------------------------------------------------- 1 | {"a", "b", "c"}.len() 2 | 3 | # output: 4 | 3 5 | -------------------------------------------------------------------------------- /golden/raw/set.test: -------------------------------------------------------------------------------- 1 | {"A", "B", "C"} 2 | 3 | # output: 4 | A 5 | B 6 | C 7 | -------------------------------------------------------------------------------- /golden/rcl/list_sum.test: -------------------------------------------------------------------------------- 1 | [3, 7, 11, 21].sum() 2 | 3 | # output: 4 | 42 5 | -------------------------------------------------------------------------------- /golden/rcl/set_sum.test: -------------------------------------------------------------------------------- 1 | {3, 7, 11, 21}.sum() 2 | 3 | # output: 4 | 42 5 | -------------------------------------------------------------------------------- /golden/json/_import.txt: -------------------------------------------------------------------------------- 1 | This content is imported by std_read_file_utf8.test. 2 | -------------------------------------------------------------------------------- /golden/json/double_negation.test: -------------------------------------------------------------------------------- 1 | not (not true) 2 | 3 | # output: 4 | true 5 | -------------------------------------------------------------------------------- /golden/json/dict_len.test: -------------------------------------------------------------------------------- 1 | {"a": 0, "b": 1, "c": 2}.len() 2 | 3 | # output: 4 | 3 5 | -------------------------------------------------------------------------------- /golden/rcl/builtin_method.test: -------------------------------------------------------------------------------- 1 | "abc".len 2 | 3 | # output: 4 | «method String.len» 5 | -------------------------------------------------------------------------------- /golden/rcl/list_map.test: -------------------------------------------------------------------------------- 1 | [1, 2, 3].map(x => x * 2) 2 | 3 | # output: 4 | [2, 4, 6] 5 | -------------------------------------------------------------------------------- /golden/rcl/set_empty_std.test: -------------------------------------------------------------------------------- 1 | std.empty_set 2 | 3 | # output: 4 | std.empty_set 5 | -------------------------------------------------------------------------------- /golden/rcl/set_to_list.test: -------------------------------------------------------------------------------- 1 | {42, 1, 2}.to_list() 2 | 3 | # output: 4 | [1, 2, 42] 5 | -------------------------------------------------------------------------------- /golden/rcl/typed_let_null.test: -------------------------------------------------------------------------------- 1 | let n: Null = null; n 2 | 3 | # output: 4 | null 5 | -------------------------------------------------------------------------------- /golden/rcl/typed_let_set.test: -------------------------------------------------------------------------------- 1 | let s: Set[Number] = {0}; s 2 | 3 | # output: 4 | {0} 5 | -------------------------------------------------------------------------------- /golden/rcl/typed_let_string.test: -------------------------------------------------------------------------------- 1 | let s: String = ""; s 2 | 3 | # output: 4 | "" 5 | -------------------------------------------------------------------------------- /golden/rcl/import_relative.test: -------------------------------------------------------------------------------- 1 | (import "_import.rcl").answer 2 | 3 | # output: 4 | 42 5 | -------------------------------------------------------------------------------- /golden/rcl/list_filter.test: -------------------------------------------------------------------------------- 1 | [1, 2, 3].filter(x => x > 1) 2 | 3 | # output: 4 | [2, 3] 5 | -------------------------------------------------------------------------------- /golden/rcl/set_filter.test: -------------------------------------------------------------------------------- 1 | {1, 2, 3}.filter(x => x > 1) 2 | 3 | # output: 4 | {2, 3} 5 | -------------------------------------------------------------------------------- /golden/rcl/string_remove_prefix.test: -------------------------------------------------------------------------------- 1 | "ab".remove_prefix("a") 2 | 3 | # output: 4 | "b" 5 | -------------------------------------------------------------------------------- /golden/rcl/string_remove_suffix.test: -------------------------------------------------------------------------------- 1 | "ab".remove_suffix("b") 2 | 3 | # output: 4 | "a" 5 | -------------------------------------------------------------------------------- /golden/json/list_reverse.test: -------------------------------------------------------------------------------- 1 | ["a", "b", "c"].reverse() 2 | 3 | # output: 4 | ["c", "b", "a"] 5 | -------------------------------------------------------------------------------- /golden/json/string_replace.test: -------------------------------------------------------------------------------- 1 | "abacab".replace("ab", "ac") 2 | 3 | # output: 4 | "acacac" 5 | -------------------------------------------------------------------------------- /golden/rcl/builtin_function.test: -------------------------------------------------------------------------------- 1 | std.read_file_utf8 2 | 3 | # output: 4 | std.read_file_utf8 5 | -------------------------------------------------------------------------------- /golden/rcl/function.test: -------------------------------------------------------------------------------- 1 | (arg1, arg2) => arg1 + arg2 2 | 3 | # output: 4 | «function 0:0..28» 5 | -------------------------------------------------------------------------------- /golden/rcl/set_map_dedup.test: -------------------------------------------------------------------------------- 1 | {1, 2, 3}.map_dedup(x => x * 3) 2 | 3 | # output: 4 | {3, 6, 9} 5 | -------------------------------------------------------------------------------- /golden/types/expr_list_void.test: -------------------------------------------------------------------------------- 1 | let xs: List[Void] = []; 2 | xs 3 | 4 | # output: 5 | [] 6 | -------------------------------------------------------------------------------- /golden/rcl/import_workdir_relative.test: -------------------------------------------------------------------------------- 1 | (import "//_import.rcl").answer 2 | 3 | # output: 4 | 42 5 | -------------------------------------------------------------------------------- /golden/rcl/list_to_set_unique.test: -------------------------------------------------------------------------------- 1 | [42, 1, 2].to_set_unique() 2 | 3 | # output: 4 | {1, 2, 42} 5 | -------------------------------------------------------------------------------- /golden/rcl/set_empty_comprehension.test: -------------------------------------------------------------------------------- 1 | { for x in []: x } 2 | 3 | # output: 4 | std.empty_set 5 | -------------------------------------------------------------------------------- /golden/cmd/query_basic.test: -------------------------------------------------------------------------------- 1 | # command: ["query", "input.a"] 2 | { a = 42 } 3 | 4 | # output: 5 | 42 6 | -------------------------------------------------------------------------------- /golden/json/empty_dict.test: -------------------------------------------------------------------------------- 1 | // This is an empty dict, not an empty set. 2 | {} 3 | 4 | # output: 5 | {} 6 | -------------------------------------------------------------------------------- /golden/json/list_join.test: -------------------------------------------------------------------------------- 1 | ["one", 2, "three"].join(" -- ") 2 | 3 | # output: 4 | "one -- 2 -- three" 5 | -------------------------------------------------------------------------------- /golden/rcl/list_to_set_dedup.test: -------------------------------------------------------------------------------- 1 | [42, 1, 2, 1, 42].to_set_dedup() 2 | 3 | # output: 4 | {1, 2, 42} 5 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "docs/theme"] 2 | path = docs/theme 3 | url = https://github.com/ruuda/kilsbergen 4 | -------------------------------------------------------------------------------- /golden/json/field.test: -------------------------------------------------------------------------------- 1 | let outer = { 2 | inner = true, 3 | }; 4 | outer.inner 5 | 6 | # output: 7 | true 8 | -------------------------------------------------------------------------------- /golden/rcl/list_enumerate.test: -------------------------------------------------------------------------------- 1 | ["x", "y", "z"].enumerate() 2 | 3 | # output: 4 | { 0: "x", 1: "y", 2: "z" } 5 | -------------------------------------------------------------------------------- /golden/rcl/typed_let_dict.test: -------------------------------------------------------------------------------- 1 | let d: Dict[String, Number] = { a = 0 }; d 2 | 3 | # output: 4 | { a = 0 } 5 | -------------------------------------------------------------------------------- /golden/cmd/patch_basic.test: -------------------------------------------------------------------------------- 1 | # command: ["patch", "-", "a", "1"] 2 | { a = 0 } 3 | 4 | # output: 5 | { a = 1 } 6 | -------------------------------------------------------------------------------- /golden/fmt/empty_collections.test: -------------------------------------------------------------------------------- 1 | [ 2 | [ 3 | ], 4 | { 5 | } 6 | ] 7 | 8 | # output: 9 | [[], {}] 10 | -------------------------------------------------------------------------------- /golden/rcl/set_except.test: -------------------------------------------------------------------------------- 1 | let s = {1, 2, 3}; 2 | [s.except(2), s.except(4)] 3 | 4 | # output: 5 | [{1, 3}, {1, 2, 3}] 6 | -------------------------------------------------------------------------------- /golden/rcl/string_parse_int.test: -------------------------------------------------------------------------------- 1 | [ 2 | "42".parse_int(), 3 | "-42".parse_int(), 4 | ] 5 | 6 | # output: 7 | [42, -42] 8 | -------------------------------------------------------------------------------- /golden/cmd/_input_fmt_err.rcl: -------------------------------------------------------------------------------- 1 | // See also _input_fmt_ok.rcl, which is formatted correctly. 2 | // This current file is not. 3 | {a=0} 4 | -------------------------------------------------------------------------------- /golden/error/lex_unmatched_rbrace.test: -------------------------------------------------------------------------------- 1 | } 2 | 3 | # output: 4 | stdin:1:1 5 | ╷ 6 | 1 │ } 7 | ╵ ^ 8 | Error: Found unmatched '}'. 9 | -------------------------------------------------------------------------------- /golden/error/lex_unmatched_rparen.test: -------------------------------------------------------------------------------- 1 | ) 2 | 3 | # output: 4 | stdin:1:1 5 | ╷ 6 | 1 │ ) 7 | ╵ ^ 8 | Error: Found unmatched ')'. 9 | -------------------------------------------------------------------------------- /golden/json/function_first_class.test: -------------------------------------------------------------------------------- 1 | let apply_twice = (f, x) => f(f(x)); 2 | apply_twice(x => x * x, 4) 3 | 4 | # output: 5 | 256 6 | -------------------------------------------------------------------------------- /golden/json/list_contains.test: -------------------------------------------------------------------------------- 1 | let xs = ["a", "b", "c"]; 2 | [xs.contains("a"), xs.contains("d")] 3 | 4 | # output: 5 | [true, false] 6 | -------------------------------------------------------------------------------- /golden/json/set_contains.test: -------------------------------------------------------------------------------- 1 | let xs = {"a", "b", "c"}; 2 | [xs.contains("a"), xs.contains("d")] 3 | 4 | # output: 5 | [true, false] 6 | -------------------------------------------------------------------------------- /golden/json/unambiguous_unop.test: -------------------------------------------------------------------------------- 1 | [ 2 | not (true and false), 3 | (not true) and false, 4 | ] 5 | 6 | # output: 7 | [true, false] 8 | -------------------------------------------------------------------------------- /pyrcl/test.rcl: -------------------------------------------------------------------------------- 1 | { 2 | name = "Import Test Data", 3 | description = "This is only here to test `rcl.load_file` in Python.", 4 | } 5 | -------------------------------------------------------------------------------- /golden/error/lex_unmatched_rbracket.test: -------------------------------------------------------------------------------- 1 | ] 2 | 3 | # output: 4 | stdin:1:1 5 | ╷ 6 | 1 │ ] 7 | ╵ ^ 8 | Error: Found unmatched ']'. 9 | -------------------------------------------------------------------------------- /golden/rcl/set_unpack_runtime_check.test: -------------------------------------------------------------------------------- 1 | let xs: Any = {1, 2, 3}; 2 | let ys: Set[Number] = {..xs}; 3 | ys 4 | 5 | # output: 6 | {1, 2, 3} 7 | -------------------------------------------------------------------------------- /golden/types/static_union_supertype_ok.test: -------------------------------------------------------------------------------- 1 | let a0: Union[Null, Number] = null; 2 | let b0: Any = a0; 3 | b0 4 | 5 | # output: 6 | null 7 | -------------------------------------------------------------------------------- /golden/cmd/patch_check_unchanged.test: -------------------------------------------------------------------------------- 1 | # command: ["patch", "-", "a", "0", "--check"] 2 | { a = 0 } 3 | 4 | # output: 5 | The patch is a no-op. 6 | -------------------------------------------------------------------------------- /golden/error/runtime_division_by_zero.test: -------------------------------------------------------------------------------- 1 | 1 / 0 2 | 3 | # output: 4 | stdin:1:3 5 | ╷ 6 | 1 │ 1 / 0 7 | ╵ ^ 8 | Error: Division by zero. 9 | -------------------------------------------------------------------------------- /golden/rcl/list_unpack_runtime_check.test: -------------------------------------------------------------------------------- 1 | let xs: Any = [1, 2, 3]; 2 | let ys: List[Number] = [..xs]; 3 | ys 4 | 5 | # output: 6 | [1, 2, 3] 7 | -------------------------------------------------------------------------------- /golden/types/runtime_union_subtype_ok.test: -------------------------------------------------------------------------------- 1 | let x: Union[Number, String] = 0; 2 | let y: Union[Bool, Number] = x; 3 | y 4 | 5 | # output: 6 | 0 7 | -------------------------------------------------------------------------------- /golden/cmd/format_check_unchanged.test: -------------------------------------------------------------------------------- 1 | # command: ["format", "-", "--check"] 2 | { a = 0 } 3 | 4 | # output: 5 | The file is formatted correctly. 6 | -------------------------------------------------------------------------------- /golden/error/parse_expected_expression.test: -------------------------------------------------------------------------------- 1 | * 2 | 3 | # output: 4 | stdin:1:1 5 | ╷ 6 | 1 │ * 7 | ╵ ^ 8 | Error: Expected an expression here. 9 | -------------------------------------------------------------------------------- /golden/json/std_read_file_utf8.test: -------------------------------------------------------------------------------- 1 | std.read_file_utf8("_import.txt") 2 | 3 | # output: 4 | "This content is imported by std_read_file_utf8.test.\n" 5 | -------------------------------------------------------------------------------- /golden/rcl/dict_keys.test: -------------------------------------------------------------------------------- 1 | let d = { 2 | a = 10, 3 | b = 20, 4 | c = 30, 5 | }; 6 | d.keys() 7 | 8 | # output: 9 | {"a", "b", "c"} 10 | -------------------------------------------------------------------------------- /golden/rcl/dict_values.test: -------------------------------------------------------------------------------- 1 | let d = { 2 | a = 10, 3 | b = 20, 4 | c = 30, 5 | }; 6 | d.values() 7 | 8 | # output: 9 | [10, 20, 30] 10 | -------------------------------------------------------------------------------- /golden/cmd/format_check_cli_unchanged.test: -------------------------------------------------------------------------------- 1 | # command: ["format", "--check", "_input_fmt_ok.rcl"] 2 | 3 | # output: 4 | The file is formatted correctly. 5 | -------------------------------------------------------------------------------- /golden/error/runtime_unknown_variable.test: -------------------------------------------------------------------------------- 1 | unknown 2 | 3 | # output: 4 | stdin:1:1 5 | ╷ 6 | 1 │ unknown 7 | ╵ ^~~~~~~ 8 | Error: Unknown variable. 9 | -------------------------------------------------------------------------------- /golden/json/dict_contains.test: -------------------------------------------------------------------------------- 1 | let xs = { 2 | "a": 1, 3 | "b": 2, 4 | }; 5 | [xs.contains("a"), xs.contains("c")] 6 | 7 | # output: 8 | [true, false] 9 | -------------------------------------------------------------------------------- /golden/raw/string.test: -------------------------------------------------------------------------------- 1 | "In raw output,\nwe get the contents of this string." 2 | 3 | # output: 4 | In raw output, 5 | we get the contents of this string. 6 | -------------------------------------------------------------------------------- /golden/rcl/dict_non_string_keys.test: -------------------------------------------------------------------------------- 1 | { 2 | 0: "zero", 3 | 1: "one", 4 | 1 + 1: "two", 5 | } 6 | 7 | # output: 8 | { 0: "zero", 1: "one", 2: "two" } 9 | -------------------------------------------------------------------------------- /golden/rcl/import_in_list.test: -------------------------------------------------------------------------------- 1 | [ 2 | import "_import.rcl", 3 | import "_import.rcl", 4 | ] 5 | 6 | # output: 7 | [{ answer = 42 }, { answer = 42 }] 8 | -------------------------------------------------------------------------------- /golden/json/integer_comparisons.test: -------------------------------------------------------------------------------- 1 | [ 2 | 0 < 1, 3 | 0 <= 1, 4 | 0 > 1, 5 | 0 >= 1, 6 | ] 7 | 8 | # output: 9 | [true, true, false, false] 10 | -------------------------------------------------------------------------------- /golden/rcl/dict_unpack.test: -------------------------------------------------------------------------------- 1 | let d1 = { a = 2, b = 3 }; 2 | let d2 = { b = 5, c = 7 }; 3 | 4 | { ...d1, ...d2 } 5 | 6 | # output: 7 | { a = 2, b = 5, c = 7 } 8 | -------------------------------------------------------------------------------- /golden/error/lex_number_empty_0b.test: -------------------------------------------------------------------------------- 1 | 0b 2 | 3 | # output: 4 | stdin:1:1 5 | ╷ 6 | 1 │ 0b 7 | ╵ ^~ 8 | Error: Expected a binary digit after 0b in this number. 9 | -------------------------------------------------------------------------------- /golden/rcl/set_empty_annotation.test: -------------------------------------------------------------------------------- 1 | // Without type annotation, this would be a dict. 2 | let empty: Set[Bool] = {}; 3 | empty 4 | 5 | # output: 6 | std.empty_set 7 | -------------------------------------------------------------------------------- /golden/rcl/set_floats.test: -------------------------------------------------------------------------------- 1 | // This is a regression test for an overflow discovered by the fuzzer. 2 | { 5e-6504, 5e26505 } 3 | 4 | # output: 5 | {5e-6504, 5e26505} 6 | -------------------------------------------------------------------------------- /golden/error/lex_number_empty_0x.test: -------------------------------------------------------------------------------- 1 | 0x 2 | 3 | # output: 4 | stdin:1:1 5 | ╷ 6 | 1 │ 0x 7 | ╵ ^~ 8 | Error: Expected a hexadecimal digit after 0x in this number. 9 | -------------------------------------------------------------------------------- /golden/error/parse_content_after_main.test: -------------------------------------------------------------------------------- 1 | 0 0 2 | 3 | # output: 4 | stdin:1:3 5 | ╷ 6 | 1 │ 0 0 7 | ╵ ^ 8 | Error: Unexpected content after the main expression. 9 | -------------------------------------------------------------------------------- /golden/error/parse_let_non_ident.test: -------------------------------------------------------------------------------- 1 | let 1 = 1; 1 2 | 3 | # output: 4 | stdin:1:5 5 | ╷ 6 | 1 │ let 1 = 1; 1 7 | ╵ ^ 8 | Error: Expected an identifier here. 9 | -------------------------------------------------------------------------------- /golden/error/parse_let_not_eq.test: -------------------------------------------------------------------------------- 1 | let x be 32; x 2 | 3 | # output: 4 | stdin:1:7 5 | ╷ 6 | 1 │ let x be 32; x 7 | ╵ ^~ 8 | Error: Expected '=' or ':' here. 9 | -------------------------------------------------------------------------------- /golden/error_json/lambda_function.test: -------------------------------------------------------------------------------- 1 | () => 1 2 | 3 | # output: 4 | stdin:1:1 5 | ╷ 6 | 1 │ () => 1 7 | ╵ ^~~~~~~ 8 | Error: Functions cannot be exported as json. 9 | -------------------------------------------------------------------------------- /golden/json/number_exponent.test: -------------------------------------------------------------------------------- 1 | [ 2 | 1e6, 3 | // This is a regression test, previously "1e0" evaluated to "0.1e1". 4 | 1e0, 5 | ] 6 | 7 | # output: 8 | [1e6, 1] 9 | -------------------------------------------------------------------------------- /golden/types/static_union_subtype_union_ok.test: -------------------------------------------------------------------------------- 1 | let a0: Union[Null, Number] = null; 2 | let a1: Union[Null, Number] = 0; 3 | [a0, a1] 4 | 5 | # output: 6 | [null, 0] 7 | -------------------------------------------------------------------------------- /golden/cmd/format_check_changed.test: -------------------------------------------------------------------------------- 1 | # command: ["format", "-", "--check"] 2 | {a=0} 3 | 4 | # output: 5 | Would modify stdin 6 | Error: 1 of 1 files would be reformatted. 7 | -------------------------------------------------------------------------------- /golden/error/lex_unrecognized_punctuation.test: -------------------------------------------------------------------------------- 1 | true @ false 2 | 3 | # output: 4 | stdin:1:6 5 | ╷ 6 | 1 │ true @ false 7 | ╵ ^ 8 | Error: Unrecognized punctuation here. 9 | -------------------------------------------------------------------------------- /golden/json/integer_arithmetic.test: -------------------------------------------------------------------------------- 1 | [ 2 | 18 + 24, 3 | 2 * 3 * 7, 4 | 50 - 8, 5 | 126 / 3, 6 | (-21) * (-2), 7 | ] 8 | 9 | # output: 10 | [42, 42, 42, 42, 42] 11 | -------------------------------------------------------------------------------- /golden/json/set_to_array.test: -------------------------------------------------------------------------------- 1 | // When exported to json, a set becomes a list. 2 | // TODO: Preserve insertion order. 3 | {"a", "c", "b"} 4 | 5 | # output: 6 | ["a", "b", "c"] 7 | -------------------------------------------------------------------------------- /golden/error/parse_bang.test: -------------------------------------------------------------------------------- 1 | !true 2 | 3 | # output: 4 | stdin:1:1 5 | ╷ 6 | 1 │ !true 7 | ╵ ^ 8 | Error: Invalid operator. Negation is written with keyword 'not' instead of '!'. 9 | -------------------------------------------------------------------------------- /golden/fmt/number_literals.test: -------------------------------------------------------------------------------- 1 | [ 2 | 0xFFFF_FFFF, 3 | 0b0101_0101, 4 | 1E10, 5 | ] 6 | 7 | # output: 8 | [ 9 | 0xffff_ffff, 10 | 0b0101_0101, 11 | 1e10, 12 | ] 13 | -------------------------------------------------------------------------------- /golden/json/fstring_escapes.test: -------------------------------------------------------------------------------- 1 | [ 2 | f"\{{0}}", 3 | "{ inside non-f-string behaves regularly." 4 | ] 5 | 6 | # output: 7 | ["{0}", "{ inside non-f-string behaves regularly."] 8 | -------------------------------------------------------------------------------- /golden/rcl/set_sort.test: -------------------------------------------------------------------------------- 1 | { 2 | a = {42, 17, 11, 144}.sort(), 3 | b = {"foo", "bar", "baz"}.sort(), 4 | } 5 | 6 | # output: 7 | { a = [11, 17, 42, 144], b = ["bar", "baz", "foo"] } 8 | -------------------------------------------------------------------------------- /golden/cmd/patch_check_changed.test: -------------------------------------------------------------------------------- 1 | # command: ["patch", "-", "a", "1", "--check"] 2 | { a = 0 } 3 | 4 | # output: 5 | Would modify stdin 6 | Error: File would be patched or reformatted. 7 | -------------------------------------------------------------------------------- /golden/error/lex_number_end_exponent.test: -------------------------------------------------------------------------------- 1 | 123.456e 2 | 3 | # output: 4 | stdin:1:9 5 | ╷ 6 | 1 │ 123.456e 7 | ╵ ^ 8 | Error: Expected a digit of the number's exponent here. 9 | -------------------------------------------------------------------------------- /golden/json/std_range.test: -------------------------------------------------------------------------------- 1 | { 2 | non_empty = std.range(-4, 6), 3 | empty = std.range(6, -4), 4 | } 5 | 6 | # output: 7 | {"empty": [], "non_empty": [-4, -3, -2, -1, 0, 1, 2, 3, 4, 5]} 8 | -------------------------------------------------------------------------------- /golden/rcl/list_sort.test: -------------------------------------------------------------------------------- 1 | { 2 | a = [42, 17, 11, 144].sort(), 3 | b = ["foo", "bar", "baz"].sort(), 4 | } 5 | 6 | # output: 7 | { a = [11, 17, 42, 144], b = ["bar", "baz", "foo"] } 8 | -------------------------------------------------------------------------------- /golden/error/_import_clean_env.rcl: -------------------------------------------------------------------------------- 1 | // This variable is not defined here. Even if it is defined at the place where 2 | // we import this document, it should still cause evaluation to fail. 3 | x 4 | -------------------------------------------------------------------------------- /golden/error/lex_number_end_decimal_point.test: -------------------------------------------------------------------------------- 1 | 123. 2 | 3 | # output: 4 | stdin:1:4 5 | ╷ 6 | 1 │ 123. 7 | ╵ ^ 8 | Error: Expected a digit to follow the decimal point in this number. 9 | -------------------------------------------------------------------------------- /golden/error_raw/not_string_top_level.test: -------------------------------------------------------------------------------- 1 | null 2 | 3 | # output: 4 | stdin:1:1 5 | ╷ 6 | 1 │ null 7 | ╵ ^~~~ 8 | Error: Expected a string for raw output, but got non-string value: null 9 | -------------------------------------------------------------------------------- /golden/error/lex_multibyte_escape.test: -------------------------------------------------------------------------------- 1 | // This is a regression test. 2 | "\🕴︎︎" 3 | 4 | # output: 5 | stdin:2:2 6 | ╷ 7 | 2 │ "\🕴︎︎" 8 | ╵ ^~ 9 | Error: Invalid escape sequence. 10 | -------------------------------------------------------------------------------- /golden/error/parse_seq_condition_no_colon.test: -------------------------------------------------------------------------------- 1 | [if true else 0] 2 | 3 | # output: 4 | stdin:1:10 5 | ╷ 6 | 1 │ [if true else 0] 7 | ╵ ^~~~ 8 | Error: Expected ':' after the condition. 9 | -------------------------------------------------------------------------------- /golden/error/parse_type_annotation_not_type.test: -------------------------------------------------------------------------------- 1 | let x: 32 = 32; 2 | x 3 | 4 | # output: 5 | stdin:1:8 6 | ╷ 7 | 1 │ let x: 32 = 32; 8 | ╵ ^~ 9 | Error: Expected a type here. 10 | -------------------------------------------------------------------------------- /golden/rcl/types_meet_null.test: -------------------------------------------------------------------------------- 1 | // This tests inference of the collection type arguments, 2 | // and in particular meet() with null on both sides. 3 | [null, null] 4 | 5 | # output: 6 | [null, null] 7 | -------------------------------------------------------------------------------- /golden/error/lex_number_end_exponent_multibyte.test: -------------------------------------------------------------------------------- 1 | 123.456e🕴︎︎ 2 | 3 | # output: 4 | stdin:1:9 5 | ╷ 6 | 1 │ 123.456e🕴︎︎ 7 | ╵ ^ 8 | Error: Expected a digit of the number's exponent here. 9 | -------------------------------------------------------------------------------- /golden/error/parse_assert_no_colon.test: -------------------------------------------------------------------------------- 1 | assert false 2 | 0 3 | 4 | # output: 5 | stdin:2:1 6 | ╷ 7 | 2 │ 0 8 | ╵ ^ 9 | Error: Expected ':' here between the assertion condition and message. 10 | -------------------------------------------------------------------------------- /golden/fmt/drop_leading_blank_lines.test: -------------------------------------------------------------------------------- 1 | 2 | 3 | // This file has leading blanks that should be dropped. 4 | true 5 | 6 | # output: 7 | // This file has leading blanks that should be dropped. 8 | true 9 | -------------------------------------------------------------------------------- /golden/json/dict_except.test: -------------------------------------------------------------------------------- 1 | let d = { 2 | a = 10, 3 | b = 20, 4 | c = 30, 5 | }; 6 | [d.except("b"), d.except("q")] 7 | 8 | # output: 9 | [{"a": 10, "c": 30}, {"a": 10, "b": 20, "c": 30}] 10 | -------------------------------------------------------------------------------- /golden/rcl/list_flat_map.test: -------------------------------------------------------------------------------- 1 | let apps = [ 2 | { name = "sshd", ports = [22] }, 3 | { name = "nginx", ports = [80, 443] }, 4 | ]; 5 | apps.flat_map(app => app.ports) 6 | 7 | # output: 8 | [22, 80, 443] 9 | -------------------------------------------------------------------------------- /golden/types/static_not_fn_1.test: -------------------------------------------------------------------------------- 1 | "frobnicator"() 2 | 3 | # output: 4 | stdin:1:1 5 | ╷ 6 | 1 │ "frobnicator"() 7 | ╵ ^~~~~~~~~~~~~ 8 | Error: This cannot be called. Expected function but found String. 9 | -------------------------------------------------------------------------------- /golden/error/parse_seq_invalid_terminator_comma.test: -------------------------------------------------------------------------------- 1 | [let x = 32; x; 10] 2 | 3 | # output: 4 | stdin:1:15 5 | ╷ 6 | 1 │ [let x = 32; x; 10] 7 | ╵ ^ 8 | Error: Expected ',' instead of ';' here. 9 | -------------------------------------------------------------------------------- /golden/error/runtime_index_out_of_bounds.test: -------------------------------------------------------------------------------- 1 | let xs = [0, 1, 2]; 2 | xs[3] 3 | 4 | # output: 5 | stdin:2:4 6 | ╷ 7 | 2 │ xs[3] 8 | ╵ ^ 9 | Error: Index 3 is out of bounds for list of length 3. 10 | -------------------------------------------------------------------------------- /golden/fmt/unop.test: -------------------------------------------------------------------------------- 1 | let x = 2 | not 3 | not 4 | not 5 | not 6 | true; 7 | 8 | let y =- 32; 9 | 10 | null 11 | 12 | # output: 13 | let x = not not not not true; 14 | 15 | let y = -32; 16 | 17 | null 18 | -------------------------------------------------------------------------------- /golden/json/conditional.test: -------------------------------------------------------------------------------- 1 | [ 2 | // For now, both with and without colon is allowed. 3 | (if true: "true" else "false"), 4 | (if false: "true" else: "false"), 5 | ] 6 | 7 | # output: 8 | ["true", "false"] 9 | -------------------------------------------------------------------------------- /golden/json/dict_comprehension.test: -------------------------------------------------------------------------------- 1 | let xs = ["apple", "orange", "pear"]; 2 | { 3 | for x in xs: 4 | let n = x.len(); 5 | if n > 4: 6 | x: n 7 | } 8 | 9 | # output: 10 | {"apple": 5, "orange": 6} 11 | -------------------------------------------------------------------------------- /golden/rcl/int_add.test: -------------------------------------------------------------------------------- 1 | // This is a regression test for the typechecker. 2 | let xs = [20]; 3 | let ys: List[Any] = [20]; 4 | [ 5 | xs[0] + 22, 6 | ys[0] + 22, 7 | ] 8 | 9 | # output: 10 | [42, 42] 11 | -------------------------------------------------------------------------------- /golden/types/runtime_union_subtype_defer_ok.test: -------------------------------------------------------------------------------- 1 | let x: Union[Any, Number] = 0; 2 | // Causes a runtime check, `Any` may not hold a valid value for `Number`. 3 | let y: Number = x; 4 | y 5 | 6 | # output: 7 | 0 8 | -------------------------------------------------------------------------------- /golden/cmd/format_check_cli_changed.test: -------------------------------------------------------------------------------- 1 | # command: ["format", "--check", "_input_fmt_ok.rcl", "_input_fmt_err.rcl"] 2 | 3 | # output: 4 | Would modify _input_fmt_err.rcl 5 | Error: 1 of 2 files would be reformatted. 6 | -------------------------------------------------------------------------------- /golden/cmd/patch_error_not_found_top_level.test: -------------------------------------------------------------------------------- 1 | # command: ["patch", "-", "b", "1"] 2 | null 3 | 4 | # output: 5 | stdin:1:1 6 | ╷ 7 | 1 │ null 8 | ╵ ^~~~ 9 | Error: Could not find 'b' in this expression. 10 | -------------------------------------------------------------------------------- /golden/error/runtime_division_non_integer.test: -------------------------------------------------------------------------------- 1 | 2 / 3 2 | 3 | # output: 4 | stdin:1:3 5 | ╷ 6 | 1 │ 2 / 3 7 | ╵ ^ 8 | Error: 2 / 3 cannot be represented exactly. Lossy arithmetic is not supported at this time. 9 | -------------------------------------------------------------------------------- /golden/error/runtime_not_fn_2.test: -------------------------------------------------------------------------------- 1 | "frobnicator".len()(42) 2 | 3 | # output: 4 | stdin:1:1 5 | ╷ 6 | 1 │ "frobnicator".len()(42) 7 | ╵ ^~~~~~~~~~~~~~~~~~~ 8 | Error: This is not a function, it cannot be called. 9 | -------------------------------------------------------------------------------- /golden/error/runtime_overflow_neg.test: -------------------------------------------------------------------------------- 1 | let x = (-0x7fff_ffff_ffff_ffff) - 1; 2 | -x 3 | 4 | # output: 5 | stdin:2:1 6 | ╷ 7 | 2 │ -x 8 | ╵ ^ 9 | Error: Negation of -9223372036854775808 would overflow. 10 | -------------------------------------------------------------------------------- /golden/error/runtime_unpack_assocs_list.test: -------------------------------------------------------------------------------- 1 | let xs: Any = [null]; 2 | {...xs} 3 | 4 | # output: 5 | stdin:2:5 6 | ╷ 7 | 2 │ {...xs} 8 | ╵ ^~ 9 | Error: Expected a dict to unpack, but this is a list. 10 | -------------------------------------------------------------------------------- /golden/error/runtime_unpack_assocs_set.test: -------------------------------------------------------------------------------- 1 | let xs: Any = {null}; 2 | {...xs} 3 | 4 | # output: 5 | stdin:2:5 6 | ╷ 7 | 2 │ {...xs} 8 | ╵ ^~ 9 | Error: Expected a dict to unpack, but this is a set. 10 | -------------------------------------------------------------------------------- /golden/error/unescape_ubrace_not_hex.test: -------------------------------------------------------------------------------- 1 | "Invalid: \u{nothex}" 2 | 3 | # output: 4 | stdin:1:14 5 | ╷ 6 | 1 │ "Invalid: \u{nothex}" 7 | ╵ ^ 8 | Error: Expected '}' to close Unicode escape sequence. 9 | -------------------------------------------------------------------------------- /golden/error/unescape_ubrace_unmatched.test: -------------------------------------------------------------------------------- 1 | "Invalid: \u{000a" 2 | 3 | # output: 4 | stdin:1:18 5 | ╷ 6 | 1 │ "Invalid: \u{000a" 7 | ╵ ^ 8 | Error: Expected '}' to close Unicode escape sequence. 9 | -------------------------------------------------------------------------------- /golden/types/expr_unknown_type.test: -------------------------------------------------------------------------------- 1 | let x: NonExistentType = null; 2 | x 3 | 4 | # output: 5 | stdin:1:8 6 | ╷ 7 | 1 │ let x: NonExistentType = null; 8 | ╵ ^~~~~~~~~~~~~~~ 9 | Error: Unknown type. 10 | -------------------------------------------------------------------------------- /golden/types/runtime_index_list_float.test: -------------------------------------------------------------------------------- 1 | let xs = [0, 1, 2]; 2 | xs[1.5] 3 | 4 | # output: 5 | stdin:2:4 6 | ╷ 7 | 2 │ xs[1.5] 8 | ╵ ^~~ 9 | Error: Expected list index to be an integer, but got 1.5. 10 | -------------------------------------------------------------------------------- /golden/types/static_union_subtype_fn_ok.test: -------------------------------------------------------------------------------- 1 | let f: Union[() -> Number, () -> Bool] = () => 0; 2 | 3 | // Statically ok, both union variants are a subtype. 4 | let g: () -> Any = f; 5 | g() 6 | 7 | # output: 8 | 0 9 | -------------------------------------------------------------------------------- /golden/error/typecheck_unpack_elem_not_iterable.test: -------------------------------------------------------------------------------- 1 | [..12] 2 | 3 | # output: 4 | stdin:1:4 5 | ╷ 6 | 1 │ [..12] 7 | ╵ ^~ 8 | Error: This is not iterable. Expected a collection, but got: 9 | 10 | Number 11 | -------------------------------------------------------------------------------- /golden/json/list_comprehension_list.test: -------------------------------------------------------------------------------- 1 | let xs = [1, 2, 3, 4, 5]; 2 | [ 3 | let y = 10; y, 4 | for x in xs: 5 | let double = x + x; 6 | if double > 4: 7 | x 8 | ] 9 | 10 | # output: 11 | [10, 3, 4, 5] 12 | -------------------------------------------------------------------------------- /golden/json/list_comprehension_set.test: -------------------------------------------------------------------------------- 1 | let xs = {1, 2, 3, 4, 5}; 2 | [ 3 | let y = 10; y, 4 | for x in xs: 5 | let double = x + x; 6 | if double > 4: 7 | x 8 | ] 9 | 10 | # output: 11 | [10, 3, 4, 5] 12 | -------------------------------------------------------------------------------- /golden/json/set_union.test: -------------------------------------------------------------------------------- 1 | { 2 | u0 = {"a", "b", "c"} | {"b", "d", "e"}, 3 | u1 = {"a", "b", "c"} | ["b", "d", "d", "e"], 4 | } 5 | 6 | # output: 7 | {"u0": ["a", "b", "c", "d", "e"], "u1": ["a", "b", "c", "d", "e"]} 8 | -------------------------------------------------------------------------------- /golden/toml/error_key_not_string.test: -------------------------------------------------------------------------------- 1 | { 2 | 42: "The answer", 3 | } 4 | 5 | # output: 6 | stdin:1:1 7 | ╷ 8 | 1 │ { 9 | ╵ ^ 10 | in value 11 | at key 42 12 | Error: To export as TOML, keys must be strings. 13 | -------------------------------------------------------------------------------- /golden/types/static_function_ok.test: -------------------------------------------------------------------------------- 1 | // Functions are contravariant in the argument type and covariant in the result. 2 | let f: (Any) -> Number = x => 0; 3 | let g: (Number) -> Any = f; 4 | null 5 | 6 | # output: 7 | null 8 | -------------------------------------------------------------------------------- /golden/error/lex_unmatched_lbrace_eof.test: -------------------------------------------------------------------------------- 1 | {a 2 | 3 | # output: 4 | stdin:2:1 5 | ╷ 6 | 2 │ 7 | ╵ ^ 8 | Error: Expected '}'. 9 | 10 | stdin:1:1 11 | ╷ 12 | 1 │ {a 13 | ╵ ^ 14 | Note: Unmatched '{' opened here. 15 | -------------------------------------------------------------------------------- /golden/error/lex_unmatched_lparen_eof.test: -------------------------------------------------------------------------------- 1 | (a 2 | 3 | # output: 4 | stdin:2:1 5 | ╷ 6 | 2 │ 7 | ╵ ^ 8 | Error: Expected ')'. 9 | 10 | stdin:1:1 11 | ╷ 12 | 1 │ (a 13 | ╵ ^ 14 | Note: Unmatched '(' opened here. 15 | -------------------------------------------------------------------------------- /golden/error/runtime_unpack_assocs_not_iterable.test: -------------------------------------------------------------------------------- 1 | let xs: Any = null; 2 | {...xs} 3 | 4 | # output: 5 | stdin:2:5 6 | ╷ 7 | 2 │ {...xs} 8 | ╵ ^~ 9 | Error: Expected a dict to unpack, but this is not a dict. 10 | -------------------------------------------------------------------------------- /golden/error/runtime_unpack_elems_not_iterable.test: -------------------------------------------------------------------------------- 1 | let xs: Any = null; 2 | [..xs] 3 | 4 | # output: 5 | stdin:2:4 6 | ╷ 7 | 2 │ [..xs] 8 | ╵ ^~ 9 | Error: Expected a list or set to unpack; this is not iterable. 10 | -------------------------------------------------------------------------------- /golden/error/typecheck_unpack_assoc_not_iterable.test: -------------------------------------------------------------------------------- 1 | {...12} 2 | 3 | # output: 4 | stdin:1:5 5 | ╷ 6 | 1 │ {...12} 7 | ╵ ^~ 8 | Error: This is not iterable. Expected a collection, but got: 9 | 10 | Number 11 | -------------------------------------------------------------------------------- /golden/json/dict_union.test: -------------------------------------------------------------------------------- 1 | let x = { a = 5, b = 7 }; 2 | let y = { a = 11, c = 13 }; 3 | 4 | // When duplicate keys are encountered, union prefers the right-hand side. 5 | x | y 6 | 7 | # output: 8 | {"a": 11, "b": 7, "c": 13} 9 | -------------------------------------------------------------------------------- /golden/toml/error_function_builtin.test: -------------------------------------------------------------------------------- 1 | { 2 | range = std.range, 3 | } 4 | 5 | # output: 6 | stdin:1:1 7 | ╷ 8 | 1 │ { 9 | ╵ ^ 10 | in value 11 | at key "range" 12 | Error: Functions cannot be exported as TOML. 13 | -------------------------------------------------------------------------------- /golden/error/lex_unmatched_lbracket_eof.test: -------------------------------------------------------------------------------- 1 | [a 2 | 3 | # output: 4 | stdin:2:1 5 | ╷ 6 | 2 │ 7 | ╵ ^ 8 | Error: Expected ']'. 9 | 10 | stdin:1:1 11 | ╷ 12 | 1 │ [a 13 | ╵ ^ 14 | Note: Unmatched '[' opened here. 15 | -------------------------------------------------------------------------------- /golden/error/parse_type_annotation_no_let.test: -------------------------------------------------------------------------------- 1 | let number: Number; 2 | number 3 | 4 | # output: 5 | stdin:1:19 6 | ╷ 7 | 1 │ let number: Number; 8 | ╵ ^ 9 | Error: Expected '=' after type annotation. 10 | -------------------------------------------------------------------------------- /golden/error/runtime_call_arity_dict_get_too_few.test: -------------------------------------------------------------------------------- 1 | {}.get("key") 2 | 3 | # output: 4 | stdin:1:13 5 | ╷ 6 | 1 │ {}.get("key") 7 | ╵ ^ 8 | Error: Missing argument 'default'. 'Dict.get' takes 2 arguments, but got 1. 9 | -------------------------------------------------------------------------------- /golden/error/runtime_overflow_add.test: -------------------------------------------------------------------------------- 1 | 0x7fff_ffff_ffff_ffff + 1 2 | 3 | # output: 4 | stdin:1:23 5 | ╷ 6 | 1 │ 0x7fff_ffff_ffff_ffff + 1 7 | ╵ ^ 8 | Error: Addition 9223372036854775807 + 1 would overflow. 9 | -------------------------------------------------------------------------------- /golden/error_raw/not_string_in_set.test: -------------------------------------------------------------------------------- 1 | {null} 2 | 3 | # output: 4 | stdin:1:1 5 | ╷ 6 | 1 │ {null} 7 | ╵ ^~~~~~ 8 | in value 9 | at index 0 10 | Error: Expected a string for raw output, but got non-string value: null 11 | -------------------------------------------------------------------------------- /golden/cmd/patch_error_not_found_collection.test: -------------------------------------------------------------------------------- 1 | # command: ["patch", "-", "b", "1"] 2 | { a = 0 } 3 | 4 | # output: 5 | stdin:1:1 6 | ╷ 7 | 1 │ { a = 0 } 8 | ╵ ^~~~~~~~~ 9 | Error: Could not find 'b' in this expression. 10 | -------------------------------------------------------------------------------- /golden/cmd/patch_error_not_found_statements.test: -------------------------------------------------------------------------------- 1 | # command: ["patch", "-", "b", "1"] 2 | let a = 0; 3 | null 4 | 5 | # output: 6 | stdin:2:1 7 | ╷ 8 | 2 │ null 9 | ╵ ^~~~ 10 | Error: Could not find 'b' in this expression. 11 | -------------------------------------------------------------------------------- /golden/error/runtime_not_fn_1.test: -------------------------------------------------------------------------------- 1 | let person = { name = "Deckard" }; 2 | person.name() 3 | 4 | # output: 5 | stdin:2:1 6 | ╷ 7 | 2 │ person.name() 8 | ╵ ^~~~~~~~~~~ 9 | Error: This is not a function, it cannot be called. 10 | -------------------------------------------------------------------------------- /golden/error/runtime_overflow_mul.test: -------------------------------------------------------------------------------- 1 | 0x7fff_ffff_ffff_ffff * 2 2 | 3 | # output: 4 | stdin:1:23 5 | ╷ 6 | 1 │ 0x7fff_ffff_ffff_ffff * 2 7 | ╵ ^ 8 | Error: Multiplication 9223372036854775807 * 2 would overflow. 9 | -------------------------------------------------------------------------------- /golden/error/unescape_invalid.test: -------------------------------------------------------------------------------- 1 | "Note that \* is not a valid escape sequence." 2 | 3 | # output: 4 | stdin:1:12 5 | ╷ 6 | 1 │ "Note that \* is not a valid escape sequence." 7 | ╵ ^~ 8 | Error: Invalid escape sequence. 9 | -------------------------------------------------------------------------------- /golden/error_json/non_string_key.test: -------------------------------------------------------------------------------- 1 | { 2 | 1: "first", 3 | 2: "second", 4 | } 5 | 6 | # output: 7 | stdin:1:1 8 | ╷ 9 | 1 │ { 10 | ╵ ^ 11 | in value 12 | at key 1 13 | Error: To export as json, keys must be strings. 14 | -------------------------------------------------------------------------------- /golden/rcl/string_to_lowercase.test: -------------------------------------------------------------------------------- 1 | let strings = [ 2 | "O_CREAT", 3 | "İstanbul", 4 | "BAHNHOFSTRAẞE", 5 | ]; 6 | [for str in strings: str.to_lowercase()] 7 | 8 | # output: 9 | ["o_creat", "i̇stanbul", "bahnhofstraße"] 10 | -------------------------------------------------------------------------------- /golden/types/expr_args_unknown_generic.test: -------------------------------------------------------------------------------- 1 | let x: NonExistent[Bool] = {}; 2 | x 3 | 4 | # output: 5 | stdin:1:8 6 | ╷ 7 | 1 │ let x: NonExistent[Bool] = {}; 8 | ╵ ^~~~~~~~~~~~~~~~~ 9 | Error: Unknown generic type. 10 | -------------------------------------------------------------------------------- /golden/types/static_name_hint_list.test: -------------------------------------------------------------------------------- 1 | let x: array = []; 2 | x 3 | 4 | # output: 5 | stdin:1:8 6 | ╷ 7 | 1 │ let x: array = []; 8 | ╵ ^~~~~ 9 | Error: Unknown type. 10 | 11 | Help: The list type is called 'List'. 12 | -------------------------------------------------------------------------------- /golden/types/static_name_hint_number.test: -------------------------------------------------------------------------------- 1 | let x: Int = 42; 2 | x 3 | 4 | # output: 5 | stdin:1:8 6 | ╷ 7 | 1 │ let x: Int = 42; 8 | ╵ ^~~ 9 | Error: Unknown type. 10 | 11 | Help: The number type is called 'Number'. 12 | -------------------------------------------------------------------------------- /golden/error/parse_unmatched_lbrace.test: -------------------------------------------------------------------------------- 1 | {1 1} 2 | 3 | # output: 4 | stdin:1:4 5 | ╷ 6 | 1 │ {1 1} 7 | ╵ ^ 8 | Error: Expected '}'. 9 | 10 | stdin:1:1 11 | ╷ 12 | 1 │ {1 1} 13 | ╵ ^ 14 | Note: Unmatched '{' opened here. 15 | -------------------------------------------------------------------------------- /golden/error/parse_unmatched_lbracket.test: -------------------------------------------------------------------------------- 1 | [1 1] 2 | 3 | # output: 4 | stdin:1:4 5 | ╷ 6 | 1 │ [1 1] 7 | ╵ ^ 8 | Error: Expected ']'. 9 | 10 | stdin:1:1 11 | ╷ 12 | 1 │ [1 1] 13 | ╵ ^ 14 | Note: Unmatched '[' opened here. 15 | -------------------------------------------------------------------------------- /golden/error/parse_unmatched_lparen.test: -------------------------------------------------------------------------------- 1 | (1 1) 2 | 3 | # output: 4 | stdin:1:4 5 | ╷ 6 | 1 │ (1 1) 7 | ╵ ^ 8 | Error: Expected ')'. 9 | 10 | stdin:1:1 11 | ╷ 12 | 1 │ (1 1) 13 | ╵ ^ 14 | Note: Unmatched '(' opened here. 15 | -------------------------------------------------------------------------------- /golden/error/typecheck_iter_not_iterable.test: -------------------------------------------------------------------------------- 1 | [for x in 12: x] 2 | 3 | # output: 4 | stdin:1:11 5 | ╷ 6 | 1 │ [for x in 12: x] 7 | ╵ ^~ 8 | Error: This is not iterable. Expected a collection, but got: 9 | 10 | Number 11 | -------------------------------------------------------------------------------- /golden/error/unescape_ubrace_too_short.test: -------------------------------------------------------------------------------- 1 | "Invalid: \u{} empty." 2 | 3 | # output: 4 | stdin:1:11 5 | ╷ 6 | 1 │ "Invalid: \u{} empty." 7 | ╵ ^~~~ 8 | Error: Unicode escape sequence is empty, expected at least one hex digit. 9 | -------------------------------------------------------------------------------- /golden/rcl/set_unpack.test: -------------------------------------------------------------------------------- 1 | let s1 = { 2, 3, 5 }; 2 | let s2 = { 5, 7, 11 }; 3 | { 4 | as_list = [ ..s1, ..s2 ], 5 | as_set = { ..s1, ..s2 }, 6 | } 7 | 8 | # output: 9 | { as_list = [2, 3, 5, 5, 7, 11], as_set = {2, 3, 5, 7, 11} } 10 | -------------------------------------------------------------------------------- /golden/error/lex_non_ascii.test: -------------------------------------------------------------------------------- 1 | { 2 | "éclair": "sweet", 3 | gougère = "savory"; 4 | } 5 | 6 | # output: 7 | stdin:3:7 8 | ╷ 9 | 3 │ gougère = "savory"; 10 | ╵ ^ 11 | Error: Non-ascii characters are not supported here. 12 | -------------------------------------------------------------------------------- /golden/error/lex_unmatched_lbrace.test: -------------------------------------------------------------------------------- 1 | {true] 2 | 3 | # output: 4 | stdin:1:6 5 | ╷ 6 | 1 │ {true] 7 | ╵ ^ 8 | Error: Expected '}'. 9 | 10 | stdin:1:1 11 | ╷ 12 | 1 │ {true] 13 | ╵ ^ 14 | Note: Unmatched '{' opened here. 15 | -------------------------------------------------------------------------------- /golden/error/lex_unmatched_lbracket.test: -------------------------------------------------------------------------------- 1 | [true) 2 | 3 | # output: 4 | stdin:1:6 5 | ╷ 6 | 1 │ [true) 7 | ╵ ^ 8 | Error: Expected ']'. 9 | 10 | stdin:1:1 11 | ╷ 12 | 1 │ [true) 13 | ╵ ^ 14 | Note: Unmatched '[' opened here. 15 | -------------------------------------------------------------------------------- /golden/error/lex_unmatched_lparen.test: -------------------------------------------------------------------------------- 1 | (true} 2 | 3 | # output: 4 | stdin:1:6 5 | ╷ 6 | 1 │ (true} 7 | ╵ ^ 8 | Error: Expected ')'. 9 | 10 | stdin:1:1 11 | ╷ 12 | 1 │ (true} 13 | ╵ ^ 14 | Note: Unmatched '(' opened here. 15 | -------------------------------------------------------------------------------- /golden/error/runtime_overflow_sub.test: -------------------------------------------------------------------------------- 1 | (-0x7fff_ffff_ffff_ffff) - 2 2 | 3 | # output: 4 | stdin:1:26 5 | ╷ 6 | 1 │ (-0x7fff_ffff_ffff_ffff) - 2 7 | ╵ ^ 8 | Error: Subtraction -9223372036854775807 - 2 would overflow. 9 | -------------------------------------------------------------------------------- /golden/json/string_contains.test: -------------------------------------------------------------------------------- 1 | [ 2 | "abc".contains("qabcd"), 3 | "abc".contains("abc"), 4 | "abc".contains("aqc"), 5 | "abc".contains("b"), 6 | "abc".contains(""), 7 | ] 8 | 9 | # output: 10 | [false, true, false, true, true] 11 | -------------------------------------------------------------------------------- /golden/rcl/list_unpack.test: -------------------------------------------------------------------------------- 1 | let xs = [ 2, 3, 5 ]; 2 | let ys = [ 5, 7, 11 ]; 3 | { 4 | as_list = [ ..xs, ..ys ], 5 | as_set = { ..xs, ..ys }, 6 | } 7 | 8 | # output: 9 | { as_list = [2, 3, 5, 5, 7, 11], as_set = {2, 3, 5, 7, 11} } 10 | -------------------------------------------------------------------------------- /golden/rcl/string_to_uppercase.test: -------------------------------------------------------------------------------- 1 | let strings = [ 2 | "o_creat", 3 | "i\u{0307}stanbul", 4 | "bahnhofstraße", 5 | ]; 6 | [for str in strings: str.to_uppercase()] 7 | 8 | # output: 9 | ["O_CREAT", "İSTANBUL", "BAHNHOFSTRASSE"] 10 | -------------------------------------------------------------------------------- /golden/toml/error_function_method.test: -------------------------------------------------------------------------------- 1 | { 2 | method = "Not exportable as TOML.".len 3 | } 4 | 5 | # output: 6 | stdin:1:1 7 | ╷ 8 | 1 │ { 9 | ╵ ^ 10 | in value 11 | at key "method" 12 | Error: Methods cannot be exported as TOML. 13 | -------------------------------------------------------------------------------- /golden/error/unescape_ubrace_too_long.test: -------------------------------------------------------------------------------- 1 | "Invalid: \u{0000000000}" 2 | 3 | # output: 4 | stdin:1:11 5 | ╷ 6 | 1 │ "Invalid: \u{0000000000}" 7 | ╵ ^~~~~~~~~~~~~~ 8 | Error: Unicode escape sequence too long, expected at most 6 hex digits. 9 | -------------------------------------------------------------------------------- /golden/json/string_ends_with.test: -------------------------------------------------------------------------------- 1 | [ 2 | "abc".ends_with("qabc"), 3 | "abc".ends_with("abc"), 4 | "abc".ends_with("qbc"), 5 | "abc".ends_with("c"), 6 | "abc".ends_with(""), 7 | ] 8 | 9 | # output: 10 | [false, true, false, true, true] 11 | -------------------------------------------------------------------------------- /golden/types/expr_args_dict_arity.test: -------------------------------------------------------------------------------- 1 | let x: Dict[Bool] = {}; 2 | x 3 | 4 | # output: 5 | stdin:1:8 6 | ╷ 7 | 1 │ let x: Dict[Bool] = {}; 8 | ╵ ^~~~~~~~~~ 9 | Error: Type 'Dict' takes two type parameters (key and value), but got 1. 10 | -------------------------------------------------------------------------------- /golden/types/runtime_iter_null.test: -------------------------------------------------------------------------------- 1 | let xs: Any = null; 2 | [for x in xs: "Runtime type error ..."] 3 | 4 | # output: 5 | stdin:2:11 6 | ╷ 7 | 2 │ [for x in xs: "Runtime type error ..."] 8 | ╵ ^~ 9 | Error: This is not iterable. 10 | -------------------------------------------------------------------------------- /golden/types/static_name_hint_bool.test: -------------------------------------------------------------------------------- 1 | let b: boolean = false; 2 | x 3 | 4 | # output: 5 | stdin:1:8 6 | ╷ 7 | 1 │ let b: boolean = false; 8 | ╵ ^~~~~~~ 9 | Error: Unknown type. 10 | 11 | Help: The boolean type is called 'Bool'. 12 | -------------------------------------------------------------------------------- /grammar/tree-sitter-rcl/test/corpus/special.txt: -------------------------------------------------------------------------------- 1 | ======= 2 | #!-line 3 | ======= 4 | 5 | #!/usr/bin/env -S rcl evaluate 6 | // And this is a comment. 7 | null 8 | 9 | --- 10 | 11 | (source_file 12 | (shebang) 13 | (comment) 14 | (null)) 15 | -------------------------------------------------------------------------------- /docs/pull_request_template.md: -------------------------------------------------------------------------------- 1 | * [ ] Ensure documentation is up to date 2 | * [ ] Ensure changelog is up to date 3 | * [ ] Ensure test coverage 4 | * [ ] Ensure fuzzers discover new code paths 5 | * [ ] Ensure grammars and fuzz dictionary are up to date 6 | -------------------------------------------------------------------------------- /golden/build/error_no_format.test: -------------------------------------------------------------------------------- 1 | { 2 | "out.txt": { 3 | format = "json", 4 | }, 5 | } 6 | 7 | # output: 8 | stdin:1:1 9 | ╷ 10 | 1 │ { 11 | ╵ ^ 12 | in value 13 | at key "out.txt" 14 | Error: Build targets must have a 'contents' field. 15 | -------------------------------------------------------------------------------- /golden/error/lex_hash_comment.test: -------------------------------------------------------------------------------- 1 | # This is not a comment. 2 | 3 | # output: 4 | stdin:1:1 5 | ╷ 6 | 1 │ # This is not a comment. 7 | ╵ ^ 8 | Error: Unrecognized punctuation here. 9 | 10 | Help: Comments are written with '//', not with '#'. 11 | -------------------------------------------------------------------------------- /golden/error/std_read_file_utf8_arity_too_few.test: -------------------------------------------------------------------------------- 1 | std.read_file_utf8() 2 | 3 | # output: 4 | stdin:1:20 5 | ╷ 6 | 1 │ std.read_file_utf8() 7 | ╵ ^ 8 | Error: Missing argument 'path'. 'std.read_file_utf8' takes 1 argument, but got 0. 9 | -------------------------------------------------------------------------------- /golden/fmt/collapse_blank_lines.test: -------------------------------------------------------------------------------- 1 | let t = true; 2 | 3 | 4 | // The double blank line above should be collapsed into one. 5 | t 6 | 7 | # output: 8 | let t = true; 9 | 10 | // The double blank line above should be collapsed into one. 11 | t 12 | -------------------------------------------------------------------------------- /golden/json/boolean_logic.test: -------------------------------------------------------------------------------- 1 | [ 2 | true and false, 3 | true and true, 4 | true or false, 5 | false or false, 6 | not true, 7 | not false, 8 | not not true, 9 | ] 10 | 11 | # output: 12 | [false, true, true, false, false, true, true] 13 | -------------------------------------------------------------------------------- /golden/error/parse_unmatched_lparen_lambda.test: -------------------------------------------------------------------------------- 1 | (a b) => 2 2 | 3 | # output: 4 | stdin:1:4 5 | ╷ 6 | 1 │ (a b) => 2 7 | ╵ ^ 8 | Error: Expected ')'. 9 | 10 | stdin:1:1 11 | ╷ 12 | 1 │ (a b) => 2 13 | ╵ ^ 14 | Note: Unmatched '(' opened here. 15 | -------------------------------------------------------------------------------- /golden/error/runtime_import_clean_env.test: -------------------------------------------------------------------------------- 1 | let x = "Here we define x, it is referenced in the imported file."; 2 | import "_import_clean_env.rcl" 3 | 4 | # output: 5 | _import_clean_env.rcl:3:1 6 | ╷ 7 | 3 │ x 8 | ╵ ^ 9 | Error: Unknown variable. 10 | -------------------------------------------------------------------------------- /golden/error/typecheck_unpack_assoc_invalid_list.test: -------------------------------------------------------------------------------- 1 | [1, ...{}] 2 | 3 | # output: 4 | stdin:1:5 5 | ╷ 6 | 1 │ [1, ...{}] 7 | ╵ ^~~~~ 8 | Error: Invalid dict unpack in list. 9 | 10 | Help: '...' unpacks dicts, use '..' to unpack lists and sets. 11 | -------------------------------------------------------------------------------- /rust-toolchain.toml: -------------------------------------------------------------------------------- 1 | [toolchain] 2 | channel = "1.75.0" 3 | components = ["rustfmt", "clippy"] 4 | targets = [ 5 | # Default regular build. 6 | "x86_64-unknown-linux-gnu", 7 | # For building a static binary. 8 | "x86_64-unknown-linux-musl", 9 | ] 10 | -------------------------------------------------------------------------------- /golden/build/error_no_contents.test: -------------------------------------------------------------------------------- 1 | { 2 | "out.txt": { 3 | contents = "Hello, world", 4 | }, 5 | } 6 | 7 | # output: 8 | stdin:1:1 9 | ╷ 10 | 1 │ { 11 | ╵ ^ 12 | in value 13 | at key "out.txt" 14 | Error: Build targets must have a 'format' field. 15 | -------------------------------------------------------------------------------- /golden/error/parse_fstring_double_no_hole.test: -------------------------------------------------------------------------------- 1 | f"This f-string has no holes." 2 | 3 | # output: 4 | stdin:1:1 5 | ╷ 6 | 1 │ f"This f-string has no holes." 7 | ╵ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | Error: This format string has no holes, it can be a regular string. 9 | -------------------------------------------------------------------------------- /golden/fmt/seq_comment.test: -------------------------------------------------------------------------------- 1 | [ 2 | // This comment should not force the entire seq to be tall. 3 | for x in xs: 4 | x * 2 5 | ] 6 | 7 | # output: 8 | [ 9 | // This comment should not force the entire seq to be tall. 10 | for x in xs: x * 2 11 | ] 12 | -------------------------------------------------------------------------------- /golden/json/string_starts_with.test: -------------------------------------------------------------------------------- 1 | [ 2 | "abc".starts_with("abcd"), 3 | "abc".starts_with("abc"), 4 | "abc".starts_with("abe"), 5 | "abc".starts_with("a"), 6 | "abc".starts_with(""), 7 | ] 8 | 9 | # output: 10 | [false, true, false, true, true] 11 | -------------------------------------------------------------------------------- /golden/toml/error_function_lambda.test: -------------------------------------------------------------------------------- 1 | { 2 | function = x => "Functions are not serializable as TOML.", 3 | } 4 | 5 | # output: 6 | stdin:1:1 7 | ╷ 8 | 1 │ { 9 | ╵ ^ 10 | in value 11 | at key "function" 12 | Error: Functions cannot be exported as TOML. 13 | -------------------------------------------------------------------------------- /golden/types/expr_args_set_arity.test: -------------------------------------------------------------------------------- 1 | let x: Set[Bool, Bool] = {}; 2 | x 3 | 4 | # output: 5 | stdin:1:8 6 | ╷ 7 | 1 │ let x: Set[Bool, Bool] = {}; 8 | ╵ ^~~~~~~~~~~~~~~ 9 | Error: Type 'Set' takes one type parameter (the element type), but got 2. 10 | -------------------------------------------------------------------------------- /golden/types/expr_args_union_arity_0.test: -------------------------------------------------------------------------------- 1 | let x: Union[] = {}; 2 | x 3 | 4 | # output: 5 | stdin:1:8 6 | ╷ 7 | 1 │ let x: Union[] = {}; 8 | ╵ ^~~~~~~ 9 | Error: A union type cannot be empty. 10 | 11 | Help: Use 'Void' for a type with no values. 12 | -------------------------------------------------------------------------------- /golden/types/static_call_arity_lambda1_too_many.test: -------------------------------------------------------------------------------- 1 | let f = _ => 42; 2 | f("too", "many") 3 | 4 | # output: 5 | stdin:2:10 6 | ╷ 7 | 2 │ f("too", "many") 8 | ╵ ^~~~~~ 9 | Error: Unexpected argument. The function takes 1 argument, but got 2. 10 | -------------------------------------------------------------------------------- /golden/error/runtime_import_absolute.test: -------------------------------------------------------------------------------- 1 | import "/home/user/.config/secrets.rcl" 2 | 3 | # output: 4 | stdin:1:8 5 | ╷ 6 | 1 │ import "/home/user/.config/secrets.rcl" 7 | ╵ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | Error: Importing absolute paths is not allowed. 9 | -------------------------------------------------------------------------------- /golden/error/runtime_import_not_string_literal.test: -------------------------------------------------------------------------------- 1 | let import_path = "this is a string literal"; 2 | import import_path 3 | 4 | # output: 5 | stdin:2:8 6 | ╷ 7 | 2 │ import import_path 8 | ╵ ^~~~~~~~~~~ 9 | Error: Import path must be a string literal. 10 | -------------------------------------------------------------------------------- /golden/error_json/builtin_method.test: -------------------------------------------------------------------------------- 1 | // Note, .len is an unserializable method, we don't call it! 2 | "frobnicator".len 3 | 4 | # output: 5 | stdin:2:1 6 | ╷ 7 | 2 │ "frobnicator".len 8 | ╵ ^~~~~~~~~~~~~~~~~ 9 | Error: Methods cannot be exported as json. 10 | -------------------------------------------------------------------------------- /golden/json/dict_index.test: -------------------------------------------------------------------------------- 1 | let d = { 2 | x = 10, 3 | "y z": 11, 4 | false: 12, 5 | null: 13, 6 | }; 7 | [ 8 | d["x"], 9 | d["y z"], 10 | d[false], 11 | d[null], 12 | (d | {42: 14})[21 + 21], 13 | ] 14 | 15 | # output: 16 | [10, 11, 12, 13, 14] 17 | -------------------------------------------------------------------------------- /golden/rcl/set_flat_map_dedup.test: -------------------------------------------------------------------------------- 1 | let apps = { 2 | { name = "sshd", ports = {22} }, 3 | { name = "nginx", ports = {80, 443} }, 4 | { name = "caddy", ports = {80, 443} }, 5 | }; 6 | apps.flat_map_dedup(app => app.ports) 7 | 8 | # output: 9 | {22, 80, 443} 10 | -------------------------------------------------------------------------------- /golden/types/expr_args_list_arity.test: -------------------------------------------------------------------------------- 1 | let x: List[Bool, Bool] = []; 2 | x 3 | 4 | # output: 5 | stdin:1:8 6 | ╷ 7 | 1 │ let x: List[Bool, Bool] = []; 8 | ╵ ^~~~~~~~~~~~~~~~ 9 | Error: Type 'List' takes one type parameter (the element type), but got 2. 10 | -------------------------------------------------------------------------------- /golden/types/runtime_union_any.test: -------------------------------------------------------------------------------- 1 | let x: Any = null; 2 | x | std 3 | 4 | # output: 5 | stdin:2:3 6 | ╷ 7 | 2 │ x | std 8 | ╵ ^ 9 | Error: Union operator | is not supported between these values. 10 | 11 | Help: The left-hand side must be a dict or set. 12 | -------------------------------------------------------------------------------- /golden/error/std_read_file_utf8_arity_too_many.test: -------------------------------------------------------------------------------- 1 | std.read_file_utf8("a", "b") 2 | 3 | # output: 4 | stdin:1:25 5 | ╷ 6 | 1 │ std.read_file_utf8("a", "b") 7 | ╵ ^~~ 8 | Error: Unexpected argument. 'std.read_file_utf8' takes 1 argument, but got 2. 9 | -------------------------------------------------------------------------------- /golden/error_json/builtin_function.test: -------------------------------------------------------------------------------- 1 | // Note, this is an unserializable function, we don't call it! 2 | std.read_file_utf8 3 | 4 | # output: 5 | stdin:2:1 6 | ╷ 7 | 2 │ std.read_file_utf8 8 | ╵ ^~~~~~~~~~~~~~~~~~ 9 | Error: Functions cannot be exported as json. 10 | -------------------------------------------------------------------------------- /golden/error/parse_let_no_semicolon.test: -------------------------------------------------------------------------------- 1 | let x = 10 2 | x 3 | 4 | # output: 5 | stdin:2:1 6 | ╷ 7 | 2 │ x 8 | ╵ ^ 9 | Error: Expected ';' here to close the let-binding. 10 | 11 | stdin:1:1 12 | ╷ 13 | 1 │ let x = 10 14 | ╵ ^~~ 15 | Note: Let-binding opened here. 16 | -------------------------------------------------------------------------------- /golden/rcl/set_sort_by.test: -------------------------------------------------------------------------------- 1 | let characters = { 2 | "Rachael", 3 | "Rick Deckard", 4 | "Gaff", 5 | "Pris", 6 | "Eldon Tyrell", 7 | }; 8 | characters.sort_by(name => name.len()) 9 | 10 | # output: 11 | ["Gaff", "Pris", "Rachael", "Eldon Tyrell", "Rick Deckard"] 12 | -------------------------------------------------------------------------------- /golden/rcl/string_split.test: -------------------------------------------------------------------------------- 1 | { 2 | a = "foo, bar, baz".split(", "), 3 | b = "|x|y|z".split("|"), 4 | c = "x&y&&z&".split("&"), 5 | } 6 | 7 | # output: 8 | { 9 | a = ["foo", "bar", "baz"], 10 | b = ["", "x", "y", "z"], 11 | c = ["x", "y", "", "z", ""], 12 | } 13 | -------------------------------------------------------------------------------- /golden/toml/top_level_set_of_dict.test: -------------------------------------------------------------------------------- 1 | // Same shape as the list test, but this time it's a set in RCL. 2 | // It should still turn into a list in TOML. The values have 3 | // been simplified a bit. 4 | { 5 | a = {{ x = 1 }}, 6 | } 7 | 8 | # output: 9 | [[a]] 10 | x = 1 11 | -------------------------------------------------------------------------------- /golden/cmd/patch_error_invalid_replacement.test: -------------------------------------------------------------------------------- 1 | # command: ["patch", "-", "a", "invalid replacement"] 2 | { a = 0 } 3 | 4 | # output: 5 | replacement:1:9 6 | ╷ 7 | 1 │ invalid replacement 8 | ╵ ^~~~~~~~~~~ 9 | Error: Unexpected content after the main expression. 10 | -------------------------------------------------------------------------------- /golden/cmd/patch_let_expr.test: -------------------------------------------------------------------------------- 1 | # command: ["patch", "-", "a", "1"] 2 | // Here we traverse the let expression (rather than let inside seq). 3 | let a = 0; 4 | null 5 | 6 | # output: 7 | // Here we traverse the let expression (rather than let inside seq). 8 | let a = 1; 9 | null 10 | -------------------------------------------------------------------------------- /golden/error/type_unknown_variable_fstring.test: -------------------------------------------------------------------------------- 1 | // This is a regression test for a bug introduced while writing the typechecker. 2 | f"{does_not_exist}" 3 | 4 | # output: 5 | stdin:2:4 6 | ╷ 7 | 2 │ f"{does_not_exist}" 8 | ╵ ^~~~~~~~~~~~~~ 9 | Error: Unknown variable. 10 | -------------------------------------------------------------------------------- /golden/json_lines/not_list.test: -------------------------------------------------------------------------------- 1 | { 2 | name = "Not allowed", 3 | description = "The top level value must be a list for json-lines.", 4 | } 5 | 6 | # output: 7 | stdin:1:1 8 | ╷ 9 | 1 │ { 10 | ╵ ^ 11 | Error: To format as JSON Lines, the top-level value must be a list. 12 | -------------------------------------------------------------------------------- /golden/toml/error_not_dict.test: -------------------------------------------------------------------------------- 1 | "This cannot directly be formatted as TOML." 2 | 3 | # output: 4 | stdin:1:1 5 | ╷ 6 | 1 │ "This cannot directly be formatted as TOML." 7 | ╵ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | Error: To format as TOML, the top-level value must be a dict. 9 | -------------------------------------------------------------------------------- /golden/types/static_index_list_not_number.test: -------------------------------------------------------------------------------- 1 | let xs = [0, 1, 2]; 2 | xs["invalid"] 3 | 4 | # output: 5 | stdin:2:4 6 | ╷ 7 | 2 │ xs["invalid"] 8 | ╵ ^~~~~~~~~ 9 | Error: Type mismatch. Expected Number but found String. 10 | 11 | Help: List indices must be integers. 12 | -------------------------------------------------------------------------------- /golden/yaml_stream/not_list.test: -------------------------------------------------------------------------------- 1 | { 2 | name = "Not allowed", 3 | description = "The top level value must be a list for yaml-stream.", 4 | } 5 | 6 | # output: 7 | stdin:1:1 8 | ╷ 9 | 1 │ { 10 | ╵ ^ 11 | Error: To format as YAML stream, the top-level value must be a list. 12 | -------------------------------------------------------------------------------- /fuzz/fuzz_targets/fuzz_cli.rs: -------------------------------------------------------------------------------- 1 | #![no_main] 2 | 3 | //! This fuzzer tests that the CLI parser does not crash whatever the input. 4 | 5 | use libfuzzer_sys::fuzz_target; 6 | 7 | use rcl::cli; 8 | 9 | fuzz_target!(|input: Vec| { 10 | let _ = cli::parse(input); 11 | }); 12 | -------------------------------------------------------------------------------- /golden/error/unescape_uppercase.test: -------------------------------------------------------------------------------- 1 | "Valid: \u000A; Invalid: \U000A." 2 | 3 | # output: 4 | stdin:1:26 5 | ╷ 6 | 1 │ "Valid: \u000A; Invalid: \U000A." 7 | ╵ ^~ 8 | Error: Invalid escape sequence. 9 | 10 | Help: Escape sequences are written lowercase. 11 | -------------------------------------------------------------------------------- /golden/json/dict_get.test: -------------------------------------------------------------------------------- 1 | let release_years = { 2 | "Blade Runner": 1982, 3 | "Blade Runner 2049": 2017, 4 | }; 5 | [ 6 | release_years.get("Blade Runner", 2000), 7 | release_years.get("Do Androids Dream of Electric Sheep", 2000), 8 | ] 9 | 10 | # output: 11 | [1982, 2000] 12 | -------------------------------------------------------------------------------- /golden/types/runtime_index_null.test: -------------------------------------------------------------------------------- 1 | let x: Any = null; 2 | x[0] 3 | 4 | # output: 5 | stdin:2:2 6 | ╷ 7 | 2 │ x[0] 8 | ╵ ^ 9 | Error: Indexing is not supported here. 10 | 11 | stdin:2:1 12 | ╷ 13 | 2 │ x[0] 14 | ╵ ^ 15 | Note: Expected a dict or list, but found: null. 16 | -------------------------------------------------------------------------------- /golden/types/static_listcomp_condition_not_bool.test: -------------------------------------------------------------------------------- 1 | [if 0: "false"] 2 | 3 | # output: 4 | stdin:1:5 5 | ╷ 6 | 1 │ [if 0: "false"] 7 | ╵ ^ 8 | Error: Type mismatch. Expected Bool but found Number. 9 | 10 | Help: There is no implicit conversion, conditions must be boolean. 11 | -------------------------------------------------------------------------------- /golden/types/static_name_hint_dict.test: -------------------------------------------------------------------------------- 1 | let x: Map[String, String] = {}; 2 | x 3 | 4 | # output: 5 | stdin:1:8 6 | ╷ 7 | 1 │ let x: Map[String, String] = {}; 8 | ╵ ^~~~~~~~~~~~~~~~~~~ 9 | Error: Unknown generic type. 10 | 11 | Help: The dictionary type is called 'Dict'. 12 | -------------------------------------------------------------------------------- /golden/error/lex_ascii_control.test: -------------------------------------------------------------------------------- 1 | let _ = "This line ends with U+0007, ASCII BEL." 2 | 3 | # output: 4 | stdin:1:49 5 | ╷ 6 | 1 │ let _ = "This line ends with U+0007, ASCII BEL." 7 | ╵ ^ 8 | Error: Control characters are not supported here. 9 | -------------------------------------------------------------------------------- /golden/error/parse_integer_overflow_hex.test: -------------------------------------------------------------------------------- 1 | [ 2 | // Still okay: 3 | 0x7fffffffffffffff, 4 | // Overflow: 5 | 0x8000000000000000, 6 | ] 7 | 8 | # output: 9 | stdin:5:3 10 | ╷ 11 | 5 │ 0x8000000000000000, 12 | ╵ ^~~~~~~~~~~~~~~~~~ 13 | Error: Overflow in integer literal. 14 | -------------------------------------------------------------------------------- /golden/error/typecheck_collection_list_kv.test: -------------------------------------------------------------------------------- 1 | ["key": "value"] 2 | 3 | # output: 4 | stdin:1:7 5 | ╷ 6 | 1 │ ["key": "value"] 7 | ╵ ^ 8 | Error: Expected single element, not key-value. 9 | 10 | Help: Key-value pairs are allowed in dicts, which are enclosed in '{}', not '[]'. 11 | -------------------------------------------------------------------------------- /golden/types/static_number_neg_inner.test: -------------------------------------------------------------------------------- 1 | -false 2 | 3 | # output: 4 | stdin:1:2 5 | ╷ 6 | 1 │ -false 7 | ╵ ^~~~~ 8 | Error: Type mismatch. Expected Number but found Bool. 9 | 10 | stdin:1:1 11 | ╷ 12 | 1 │ -false 13 | ╵ ^ 14 | Note: Expected Number because of this operator. 15 | -------------------------------------------------------------------------------- /golden/error/parse_integer_overflow_dec.test: -------------------------------------------------------------------------------- 1 | [ 2 | // Still okay: 3 | 9223372036854775807, 4 | // Overflow: 5 | 9223372036854775808, 6 | ] 7 | 8 | # output: 9 | stdin:5:3 10 | ╷ 11 | 5 │ 9223372036854775808, 12 | ╵ ^~~~~~~~~~~~~~~~~~~ 13 | Error: Overflow in number literal. 14 | -------------------------------------------------------------------------------- /golden/error/runtime_call_arity_dict_get_too_many.test: -------------------------------------------------------------------------------- 1 | {}.get("key", "default", "unexpected") 2 | 3 | # output: 4 | stdin:1:26 5 | ╷ 6 | 1 │ {}.get("key", "default", "unexpected") 7 | ╵ ^~~~~~~~~~~~ 8 | Error: Unexpected argument. 'Dict.get' takes 2 arguments, but got 3. 9 | -------------------------------------------------------------------------------- /golden/error/runtime_index_dict_key_absent.test: -------------------------------------------------------------------------------- 1 | let d = { x = 10, y = 20 }; 2 | d["z"] 3 | 4 | # output: 5 | stdin:2:3 6 | ╷ 7 | 2 │ d["z"] 8 | ╵ ^~~ 9 | Error: Dict does not have a key "z". 10 | 11 | stdin:2:1 12 | ╷ 13 | 2 │ d["z"] 14 | ╵ ^ 15 | Note: On value: { x = 10, y = 20 } 16 | -------------------------------------------------------------------------------- /golden/error/runtime_unpack_elems_dict.test: -------------------------------------------------------------------------------- 1 | let xs: Any = { a = 1 }; 2 | // This causes a runtime error, expected scalar elements but got dict. 3 | [..xs] 4 | 5 | # output: 6 | stdin:3:4 7 | ╷ 8 | 3 │ [..xs] 9 | ╵ ^~ 10 | Error: Expected a list or set to unpack, but this is a dict. 11 | -------------------------------------------------------------------------------- /golden/rcl/types_meet_void.test: -------------------------------------------------------------------------------- 1 | // This tests inference of the collection type arguments, 2 | // and in particular meet() with Void on the right-hand side. 3 | [ 4 | // Will be inferred Number. 5 | 0, 6 | // Will be inferred Void. 7 | for x in []: x, 8 | ] 9 | 10 | # output: 11 | [0] 12 | -------------------------------------------------------------------------------- /golden/types/static_binop_unsupported.test: -------------------------------------------------------------------------------- 1 | "0" + 1 2 | 3 | # output: 4 | stdin:1:1 5 | ╷ 6 | 1 │ "0" + 1 7 | ╵ ^~~ 8 | Error: Type mismatch. Expected Number but found String. 9 | 10 | stdin:1:5 11 | ╷ 12 | 1 │ "0" + 1 13 | ╵ ^ 14 | Note: Expected Number because of this operator. 15 | -------------------------------------------------------------------------------- /golden/types/static_condition_not_bool.test: -------------------------------------------------------------------------------- 1 | if 0: "false" else "true" 2 | 3 | # output: 4 | stdin:1:4 5 | ╷ 6 | 1 │ if 0: "false" else "true" 7 | ╵ ^ 8 | Error: Type mismatch. Expected Bool but found Number. 9 | 10 | Help: There is no implicit conversion, conditions must be boolean. 11 | -------------------------------------------------------------------------------- /golden/build/error_width_not_int_1.test: -------------------------------------------------------------------------------- 1 | { 2 | "out.txt": { 3 | contents = "", 4 | width = "80", 5 | }, 6 | } 7 | 8 | # output: 9 | stdin:1:1 10 | ╷ 11 | 1 │ { 12 | ╵ ^ 13 | in value 14 | at key "out.txt" 15 | at key "width" 16 | Error: Width must be a positive integer below 16,000. 17 | -------------------------------------------------------------------------------- /golden/error/lex_unclosed_string_literal.test: -------------------------------------------------------------------------------- 1 | "This is unclosed. 2 | 3 | # output: 4 | stdin:2:1 5 | ╷ 6 | 2 │ 7 | ╵ ^ 8 | Error: Unexpected end of input, string literal is not closed. 9 | 10 | stdin:1:1 11 | ╷ 12 | 1 │ "This is unclosed. 13 | ╵ ^ 14 | Note: String literal opened here. 15 | -------------------------------------------------------------------------------- /golden/error/parse_if_no_else.test: -------------------------------------------------------------------------------- 1 | let x = "default"; 2 | 3 | if cond: 4 | let x = "non-default"; 5 | 6 | x 7 | 8 | # output: 9 | stdin:7:1 10 | ╷ 11 | 7 │ 12 | ╵ ^ 13 | Error: Expected 'else' here. 14 | 15 | stdin:3:1 16 | ╷ 17 | 3 │ if cond: 18 | ╵ ^~ 19 | Note: To match this 'if'. 20 | -------------------------------------------------------------------------------- /golden/json/function_call.test: -------------------------------------------------------------------------------- 1 | let f0 = () => 42; 2 | let f1a = x => x; 3 | let f1b = (x) => x; 4 | let f1c = (x,) => x; 5 | let f2a = (x, y) => x + y; 6 | let f2b = (x, y,) => x + y; 7 | [f0(), f1a(43), f1b(44), f1c(45), f2a(21, 21), f2b(21, 22)] 8 | 9 | # output: 10 | [42, 43, 44, 45, 42, 43] 11 | -------------------------------------------------------------------------------- /golden/rcl/types_instance_of.test: -------------------------------------------------------------------------------- 1 | // This tests the `is_instance_of` check that gets inserted for doing dynamic 2 | // type checks. 3 | let x = null; 4 | let y: Any = x; 5 | let z: Null = y; 6 | 7 | let x = true; 8 | let y: Any = x; 9 | let z: Bool = y; 10 | 11 | z 12 | 13 | # output: 14 | true 15 | -------------------------------------------------------------------------------- /golden/types/runtime_index_string.test: -------------------------------------------------------------------------------- 1 | let str: Any = "not yet intexable"; 2 | str[0] 3 | 4 | # output: 5 | stdin:2:4 6 | ╷ 7 | 2 │ str[0] 8 | ╵ ^ 9 | Error: Indexing into a string is not yet supported. 10 | 11 | stdin:2:1 12 | ╷ 13 | 2 │ str[0] 14 | ╵ ^~~ 15 | Note: This is a string. 16 | -------------------------------------------------------------------------------- /grammar/zed/languages/rcl/brackets.scm: -------------------------------------------------------------------------------- 1 | ("[" @open "]" @close) 2 | ("{" @open "}" @close) 3 | ("(" @open ")" @close) 4 | (string_double "\"" @open "\"" @close) 5 | (string_triple "\"\"\"" @open "\"\"\"" @close) 6 | (fstring_double "f\"" @open "\"" @close) 7 | (fstring_triple "f\"\"\"" @open "\"\"\"" @close) 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Bison 2 | /grammar.html 3 | /grammar.output 4 | /grammar.tab.c 5 | /grammar.xml 6 | 7 | # Rust 8 | /target 9 | 10 | # Nix 11 | /result* 12 | 13 | # Python venv managed by Nix 14 | /.venv 15 | 16 | # MkDocs 17 | /site 18 | 19 | # `rcl build` output for the examples 20 | /examples/out 21 | -------------------------------------------------------------------------------- /golden/build/error_width_not_int_2.test: -------------------------------------------------------------------------------- 1 | { 2 | "out.txt": { 3 | contents = "", 4 | width = 33.33, 5 | }, 6 | } 7 | 8 | # output: 9 | stdin:1:1 10 | ╷ 11 | 1 │ { 12 | ╵ ^ 13 | in value 14 | at key "out.txt" 15 | at key "width" 16 | Error: Width must be a positive integer below 16,000. 17 | -------------------------------------------------------------------------------- /golden/build/error_width_out_of_range.test: -------------------------------------------------------------------------------- 1 | { 2 | "out.txt": { 3 | contents = "", 4 | width = 16_000, 5 | }, 6 | } 7 | 8 | # output: 9 | stdin:1:1 10 | ╷ 11 | 1 │ { 12 | ╵ ^ 13 | in value 14 | at key "out.txt" 15 | at key "width" 16 | Error: Width must be a positive integer below 16,000. 17 | -------------------------------------------------------------------------------- /golden/raw/list.test: -------------------------------------------------------------------------------- 1 | [ 2 | "A list of strings", 3 | "results in one line of output", 4 | "per line in the list.", 5 | "Newlines\nare still allowed.", 6 | ] 7 | 8 | # output: 9 | A list of strings 10 | results in one line of output 11 | per line in the list. 12 | Newlines 13 | are still allowed. 14 | -------------------------------------------------------------------------------- /golden/types/static_unop_unsupported.test: -------------------------------------------------------------------------------- 1 | not "true" 2 | 3 | # output: 4 | stdin:1:5 5 | ╷ 6 | 1 │ not "true" 7 | ╵ ^~~~~~ 8 | Error: Type mismatch. Expected Bool but found String. 9 | 10 | stdin:1:1 11 | ╷ 12 | 1 │ not "true" 13 | ╵ ^~~ 14 | Note: Expected Bool because of this operator. 15 | -------------------------------------------------------------------------------- /golden/build/error_banner_invalid.test: -------------------------------------------------------------------------------- 1 | { 2 | "out.txt": { 3 | // Banner must be a string or null. 4 | banner = 42, 5 | }, 6 | } 7 | 8 | # output: 9 | stdin:1:1 10 | ╷ 11 | 1 │ { 12 | ╵ ^ 13 | in value 14 | at key "out.txt" 15 | at key "banner" 16 | Error: Banner must be a string or null. 17 | -------------------------------------------------------------------------------- /golden/error/lex_number_empty_0x_underscores.test: -------------------------------------------------------------------------------- 1 | // A number that consists of only underscores is not an empty string, 2 | // yet we should reject it as a number. 3 | 0x___ 4 | 5 | # output: 6 | stdin:3:1 7 | ╷ 8 | 3 │ 0x___ 9 | ╵ ^~~~~ 10 | Error: Expected a hexadecimal digit after 0x in this number. 11 | -------------------------------------------------------------------------------- /golden/error/lex_unclosed_triple_string_literal.test: -------------------------------------------------------------------------------- 1 | """ 2 | This is unclosed as well 3 | 4 | # output: 5 | stdin:3:1 6 | ╷ 7 | 3 │ 8 | ╵ ^ 9 | Error: Unexpected end of input, string literal is not closed. 10 | 11 | stdin:1:1 12 | ╷ 13 | 1 │ """ 14 | ╵ ^~~ 15 | Note: String literal opened here. 16 | -------------------------------------------------------------------------------- /golden/error/parse_call_unmatched.test: -------------------------------------------------------------------------------- 1 | frobnicate(widget {}) 2 | 3 | # output: 4 | stdin:1:19 5 | ╷ 6 | 1 │ frobnicate(widget {}) 7 | ╵ ^ 8 | Error: Expected ')'. 9 | 10 | stdin:1:11 11 | ╷ 12 | 1 │ frobnicate(widget {}) 13 | ╵ ^ 14 | Note: Unmatched '(' opened here. 15 | -------------------------------------------------------------------------------- /golden/error/parse_string_newline.test: -------------------------------------------------------------------------------- 1 | """This is not allowed, 2 | there has to be a line break after the opening quotes.""" 3 | 4 | # output: 5 | stdin:1:4 6 | ╷ 7 | 1 │ """This is not allowed, 8 | ╵ ^~~~~~~~~~~~~~~~~~~~ 9 | Error: Expected a line break after the """. Move this to the next line. 10 | -------------------------------------------------------------------------------- /golden/error/unescape_ujson_not_scalar.test: -------------------------------------------------------------------------------- 1 | "Invalid: \ud800" 2 | 3 | # output: 4 | stdin:1:11 5 | ╷ 6 | 1 │ "Invalid: \ud800" 7 | ╵ ^~~~~~ 8 | Error: Invalid escape sequence: not a Unicode scalar value. 9 | 10 | Help: For code points beyond U+FFFF, use '\u{...}' instead of a surrogate pair. 11 | -------------------------------------------------------------------------------- /golden/build/error_unknown_field.test: -------------------------------------------------------------------------------- 1 | { 2 | "out.txt": { 3 | contents = "", 4 | unknown_field = true, 5 | }, 6 | } 7 | 8 | # output: 9 | stdin:1:1 10 | ╷ 11 | 1 │ { 12 | ╵ ^ 13 | in value 14 | at key "out.txt" 15 | at key "unknown_field" 16 | Error: Unknown build target field: 'unknown_field'. 17 | -------------------------------------------------------------------------------- /golden/error/runtime_call_arity_contains.test: -------------------------------------------------------------------------------- 1 | {"k": "v"}.contains("k", "unexpected argument") 2 | 3 | # output: 4 | stdin:1:26 5 | ╷ 6 | 1 │ {"k": "v"}.contains("k", "unexpected argument") 7 | ╵ ^~~~~~~~~~~~~~~~~~~~~ 8 | Error: Unexpected argument. 'Dict.contains' takes 1 argument, but got 2. 9 | -------------------------------------------------------------------------------- /golden/error/std_string_parse_number_empty.test: -------------------------------------------------------------------------------- 1 | "".parse_number() 2 | 3 | # output: 4 | stdin:1:1 5 | ╷ 6 | 1 │ "".parse_number() 7 | ╵ ^~ 8 | Error: Failed to parse as number: "" 9 | 10 | stdin:1:16 11 | ╷ 12 | 1 │ "".parse_number() 13 | ╵ ^ 14 | In call to method 'String.parse_number'. 15 | -------------------------------------------------------------------------------- /golden/json/trace.test: -------------------------------------------------------------------------------- 1 | // Note, in the output, stdout is first and stderr second, 2 | // which is why the trace is after the result. 3 | trace "Value to trace."; 0 4 | 5 | # output: 6 | 0 7 | stdin:3:7 8 | ╷ 9 | 3 │ trace "Value to trace."; 0 10 | ╵ ^~~~~~~~~~~~~~~~~ 11 | Trace: "Value to trace." 12 | 13 | -------------------------------------------------------------------------------- /golden/types/expr_args_union_arity_1.test: -------------------------------------------------------------------------------- 1 | let x: Union[Bool] = {}; 2 | x 3 | 4 | # output: 5 | stdin:1:8 6 | ╷ 7 | 1 │ let x: Union[Bool] = {}; 8 | ╵ ^~~~~~~~~~~ 9 | Error: A union type must have more than one member. 10 | 11 | Help: A union of a single type is equivalent to just that type itself. 12 | -------------------------------------------------------------------------------- /golden/error/runtime_assertion_failure.test: -------------------------------------------------------------------------------- 1 | assert true: "This assertion should pass."; 2 | assert false: "This assertion should fail."; 3 | 0 4 | 5 | # output: 6 | stdin:2:8 7 | ╷ 8 | 2 │ assert false: "This assertion should fail."; 9 | ╵ ^~~~~ 10 | Error: Assertion failed. This assertion should fail. 11 | -------------------------------------------------------------------------------- /golden/error/std_string_replace_not_string_needle.test: -------------------------------------------------------------------------------- 1 | "".replace(1, "") 2 | 3 | # output: 4 | stdin:1:12 5 | ╷ 6 | 1 │ "".replace(1, "") 7 | ╵ ^ 8 | Error: Needle must be a string. 9 | 10 | stdin:1:11 11 | ╷ 12 | 1 │ "".replace(1, "") 13 | ╵ ^ 14 | In call to method 'String.replace'. 15 | -------------------------------------------------------------------------------- /golden/error/std_string_split_empty_separator.test: -------------------------------------------------------------------------------- 1 | "xyz".split("") 2 | 3 | # output: 4 | stdin:1:13 5 | ╷ 6 | 1 │ "xyz".split("") 7 | ╵ ^~ 8 | Error: Cannot split on empty separator. 9 | 10 | stdin:1:12 11 | ╷ 12 | 1 │ "xyz".split("") 13 | ╵ ^ 14 | In call to method 'String.split'. 15 | -------------------------------------------------------------------------------- /golden/error/unescape_ubrace_not_scalar.test: -------------------------------------------------------------------------------- 1 | "Invalid: \u{d800}" 2 | 3 | # output: 4 | stdin:1:11 5 | ╷ 6 | 1 │ "Invalid: \u{d800}" 7 | ╵ ^~~~~~~~ 8 | Error: Invalid escape sequence: not a Unicode scalar value. 9 | 10 | Help: For code points beyond U+FFFF, use '\u{...}' instead of a surrogate pair. 11 | -------------------------------------------------------------------------------- /golden/rcl/string_split_lines.test: -------------------------------------------------------------------------------- 1 | { 2 | a = 3 | """ 4 | Line 1 5 | Line 2 6 | """.split_lines(), 7 | crlf1 = "a\r\nb\r\nc\r\n".split_lines(), 8 | crlf2 = "a\r\nb\r\nc".split_lines(), 9 | } 10 | 11 | # output: 12 | { a = ["Line 1", "Line 2"], crlf1 = ["a", "b", "c"], crlf2 = ["a", "b", "c"] } 13 | -------------------------------------------------------------------------------- /golden/error/runtime_import_fstring.test: -------------------------------------------------------------------------------- 1 | import f"f-strings are {"not"} allowed as import paths." 2 | 3 | # output: 4 | stdin:1:8 5 | ╷ 6 | 1 │ import f"f-strings are {"not"} allowed as import paths." 7 | ╵ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | Error: Import path must be a string literal without holes. 9 | -------------------------------------------------------------------------------- /golden/error/runtime_import_not_found.test: -------------------------------------------------------------------------------- 1 | import "this_file_does_not_exist.rcl" 2 | 3 | # output: 4 | stdin:1:8 5 | ╷ 6 | 1 │ import "this_file_does_not_exist.rcl" 7 | ╵ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | Error: Failed to access path '/WORKDIR/error/this_file_does_not_exist.rcl': No such file or directory (os error 2) 9 | -------------------------------------------------------------------------------- /golden/fmt/chain_atomic.test: -------------------------------------------------------------------------------- 1 | let x = very_long_base.very_long_field.another_long_field.call_func().list[32].entry["key"].call_again(); 2 | x 3 | 4 | # output: 5 | let x = very_long_base 6 | .very_long_field 7 | .another_long_field 8 | .call_func() 9 | .list[32] 10 | .entry["key"] 11 | .call_again(); 12 | x 13 | -------------------------------------------------------------------------------- /golden/build/build_multiple.test: -------------------------------------------------------------------------------- 1 | { 2 | "a.txt": { 3 | format = "raw", 4 | contents = "Contents of a.txt", 5 | }, 6 | 7 | "b.txt": { 8 | format = "raw", 9 | contents = "Contents of b.txt", 10 | }, 11 | } 12 | 13 | # output: 14 | [1/2] a.txt 15 | Contents of a.txt 16 | [2/2] b.txt 17 | Contents of b.txt 18 | -------------------------------------------------------------------------------- /golden/error/parse_assert_semicolon.test: -------------------------------------------------------------------------------- 1 | assert false; 2 | 0 3 | 4 | # output: 5 | stdin:1:13 6 | ╷ 7 | 1 │ assert false; 8 | ╵ ^ 9 | Error: Expected ':' here between the assertion condition and message. 10 | 11 | Help: An assertion has the form 'assert : ;'. The message is not optional. 12 | -------------------------------------------------------------------------------- /golden/error/std_list_sum_not_int.test: -------------------------------------------------------------------------------- 1 | [1, 2, false].sum() 2 | 3 | # output: 4 | stdin:1:1 5 | ╷ 6 | 1 │ [1, 2, false].sum() 7 | ╵ ^~~~~~~~~~~~~ 8 | Error: Expected integers to add, but found false. 9 | 10 | stdin:1:18 11 | ╷ 12 | 1 │ [1, 2, false].sum() 13 | ╵ ^ 14 | In call to method 'List.sum'. 15 | -------------------------------------------------------------------------------- /golden/error/std_string_contains_not_string.test: -------------------------------------------------------------------------------- 1 | "abc".contains(123) 2 | 3 | # output: 4 | stdin:1:16 5 | ╷ 6 | 1 │ "abc".contains(123) 7 | ╵ ^~~ 8 | Error: Needle must be a string. 9 | 10 | stdin:1:15 11 | ╷ 12 | 1 │ "abc".contains(123) 13 | ╵ ^ 14 | In call to method 'String.contains'. 15 | -------------------------------------------------------------------------------- /golden/error/unescape_u_not_hex.test: -------------------------------------------------------------------------------- 1 | "Invalid: \unothex" 2 | 3 | # output: 4 | stdin:1:11 5 | ╷ 6 | 1 │ "Invalid: \unothex" 7 | ╵ ^~ 8 | Error: Expected four hex digits after '\u' Unicode escape sequence. 9 | 10 | Help: You can also use up to six hex digits enclosed in '{}'. For example '\u{1F574}' or '\u{0a}'. 11 | -------------------------------------------------------------------------------- /golden/error/unescape_ujson_too_short.test: -------------------------------------------------------------------------------- 1 | "Invalid: \u00" 2 | 3 | # output: 4 | stdin:1:11 5 | ╷ 6 | 1 │ "Invalid: \u00" 7 | ╵ ^~~~ 8 | Error: Expected four hex digits after '\u' Unicode escape sequence. 9 | 10 | Help: You can also use up to six hex digits enclosed in '{}'. For example '\u{1F574}' or '\u{0a}'. 11 | -------------------------------------------------------------------------------- /golden/rcl/list_sort_by.test: -------------------------------------------------------------------------------- 1 | // Note, the sort is stable, Eldon goes at the end. 2 | let characters = [ 3 | "Rachael", 4 | "Rick Deckard", 5 | "Gaff", 6 | "Pris", 7 | "Eldon Tyrell", 8 | ]; 9 | characters.sort_by(name => name.len()) 10 | 11 | # output: 12 | ["Gaff", "Pris", "Rachael", "Rick Deckard", "Eldon Tyrell"] 13 | -------------------------------------------------------------------------------- /golden/types/runtime_union_subtype_defer_ok_2.test: -------------------------------------------------------------------------------- 1 | // This exercises the code path of checking that a non-union is a subtype 2 | // of a union, but for at least one of the union elements, we need a runtime 3 | // check. 4 | let x: List[Any] = []; 5 | let y: Union[List[Number], List[String]] = x; 6 | y 7 | 8 | # output: 9 | [] 10 | -------------------------------------------------------------------------------- /golden/types/static_call_arity_lambda1_too_few.test: -------------------------------------------------------------------------------- 1 | let f = _ => 42; 2 | f() 3 | 4 | # output: 5 | stdin:2:3 6 | ╷ 7 | 2 │ f() 8 | ╵ ^ 9 | Error: Missing argument '_'. The function takes 1 argument, but got 0. 10 | 11 | stdin:1:9 12 | ╷ 13 | 1 │ let f = _ => 42; 14 | ╵ ^ 15 | Note: Argument defined here. 16 | -------------------------------------------------------------------------------- /golden/cmd/patch_error_invalid_path.test: -------------------------------------------------------------------------------- 1 | # command: ["patch", "-", "invalid path", "1"] 2 | null 3 | 4 | # output: 5 | path:1:1 6 | ╷ 7 | 1 │ invalid path 8 | ╵ ^~~~~~~~~~~~ 9 | Error: This path segment is not a valid identifier. 10 | 11 | Help: A document path can only contain identifiers, not list indexes or arbitrary keys. 12 | -------------------------------------------------------------------------------- /golden/error/std_string_ends_with_not_string.test: -------------------------------------------------------------------------------- 1 | "abc".ends_with(123) 2 | 3 | # output: 4 | stdin:1:17 5 | ╷ 6 | 1 │ "abc".ends_with(123) 7 | ╵ ^~~ 8 | Error: Suffix must be a string. 9 | 10 | stdin:1:16 11 | ╷ 12 | 1 │ "abc".ends_with(123) 13 | ╵ ^ 14 | In call to method 'String.ends_with'. 15 | -------------------------------------------------------------------------------- /golden/error/std_string_replace_not_string_replacement.test: -------------------------------------------------------------------------------- 1 | "".replace("", 1) 2 | 3 | # output: 4 | stdin:1:16 5 | ╷ 6 | 1 │ "".replace("", 1) 7 | ╵ ^ 8 | Error: Replacement must be a string. 9 | 10 | stdin:1:11 11 | ╷ 12 | 1 │ "".replace("", 1) 13 | ╵ ^ 14 | In call to method 'String.replace'. 15 | -------------------------------------------------------------------------------- /golden/error/unescape_linebreak.test: -------------------------------------------------------------------------------- 1 | "A string can not \ 2 | be broken like this." 3 | 4 | # output: 5 | stdin:1:19 6 | ╷ 7 | 1 │ "A string can not \ 8 | ╵ ^ 9 | Error: Invalid escape sequence. 10 | 11 | Help: To break a long string across lines, break it into multiple strings and concatenate them with '+'. 12 | -------------------------------------------------------------------------------- /golden/error/unescape_ujson_not_hex.test: -------------------------------------------------------------------------------- 1 | "Invalid: \u0nothex" 2 | 3 | # output: 4 | stdin:1:11 5 | ╷ 6 | 1 │ "Invalid: \u0nothex" 7 | ╵ ^~~ 8 | Error: Expected four hex digits after '\u' Unicode escape sequence. 9 | 10 | Help: You can also use up to six hex digits enclosed in '{}'. For example '\u{1F574}' or '\u{0a}'. 11 | -------------------------------------------------------------------------------- /golden/fmt/import.test: -------------------------------------------------------------------------------- 1 | let a = 2 | import 3 | "some_file.rcl"; 4 | let very_long_variable_name = import "and_a_very_long_file_name_that_overflows_the_width.rcl"; 5 | a 6 | 7 | # output: 8 | let a = import "some_file.rcl"; 9 | let very_long_variable_name = import 10 | "and_a_very_long_file_name_that_overflows_the_width.rcl"; 11 | a 12 | -------------------------------------------------------------------------------- /golden/fmt/index_tall.test: -------------------------------------------------------------------------------- 1 | [ 2 | xs[// A comment forces tall mode in indexing. 3 | 1 4 | ], // But usually the brackets are tight. 5 | xs[2], 6 | ] 7 | 8 | 9 | # output: 10 | [ 11 | xs[ 12 | // A comment forces tall mode in indexing. 13 | 1 14 | ], 15 | // But usually the brackets are tight. 16 | xs[2], 17 | ] 18 | -------------------------------------------------------------------------------- /golden/types/static_function_defer.test: -------------------------------------------------------------------------------- 1 | // In this case, the assignment to `g`, ultimately it passes at a runtime check, 2 | // but statically, first it causes a Defer because values of `Any` *might* fit 3 | // the type `Number`. 4 | let f: (Any) -> Any = x => 0; 5 | let g: (Number) -> Number = f; 6 | null 7 | 8 | # output: 9 | null 10 | -------------------------------------------------------------------------------- /golden/types/static_index_null.test: -------------------------------------------------------------------------------- 1 | let x = null; 2 | x[0] 3 | 4 | # output: 5 | stdin:2:2 6 | ╷ 7 | 2 │ x[0] 8 | ╵ ^ 9 | Error: Indexing is not supported here. Expected a dict or list, but got: 10 | 11 | Null 12 | 13 | stdin:1:9 14 | ╷ 15 | 1 │ let x = null; 16 | ╵ ^~~~ 17 | Note: Found Null because of this value. 18 | -------------------------------------------------------------------------------- /golden/types/static_index_string.test: -------------------------------------------------------------------------------- 1 | "not yet indexable"[0] 2 | 3 | # output: 4 | stdin:1:20 5 | ╷ 6 | 1 │ "not yet indexable"[0] 7 | ╵ ^ 8 | Error: Indexing into a string is not yet supported. 9 | 10 | stdin:1:1 11 | ╷ 12 | 1 │ "not yet indexable"[0] 13 | ╵ ^~~~~~~~~~~~~~~~~~~ 14 | Note: This is a string. 15 | -------------------------------------------------------------------------------- /golden/error/std_string_parse_int.test: -------------------------------------------------------------------------------- 1 | "0xbadbeef".parse_int() 2 | 3 | # output: 4 | stdin:1:1 5 | ╷ 6 | 1 │ "0xbadbeef".parse_int() 7 | ╵ ^~~~~~~~~~~ 8 | Error: Failed to parse as integer: "0xbadbeef" 9 | 10 | stdin:1:22 11 | ╷ 12 | 1 │ "0xbadbeef".parse_int() 13 | ╵ ^ 14 | In call to method 'String.parse_int'. 15 | -------------------------------------------------------------------------------- /golden/error/std_string_parse_number_invalid_single.test: -------------------------------------------------------------------------------- 1 | "NaN".parse_number() 2 | 3 | # output: 4 | stdin:1:1 5 | ╷ 6 | 1 │ "NaN".parse_number() 7 | ╵ ^~~~~ 8 | Error: Failed to parse as number: "NaN" 9 | 10 | stdin:1:19 11 | ╷ 12 | 1 │ "NaN".parse_number() 13 | ╵ ^ 14 | In call to method 'String.parse_number'. 15 | -------------------------------------------------------------------------------- /golden/error/std_string_split_not_string.test: -------------------------------------------------------------------------------- 1 | "foo,bar,baz".split(1) 2 | 3 | # output: 4 | stdin:1:21 5 | ╷ 6 | 1 │ "foo,bar,baz".split(1) 7 | ╵ ^ 8 | Error: Separator must be a string. 9 | 10 | stdin:1:20 11 | ╷ 12 | 1 │ "foo,bar,baz".split(1) 13 | ╵ ^ 14 | In call to method 'String.split'. 15 | -------------------------------------------------------------------------------- /golden/error/typecheck_unpack_assoc_list.test: -------------------------------------------------------------------------------- 1 | let xs = [1, 2]; 2 | let ys = {...xs}; 3 | ys 4 | 5 | # output: 6 | stdin:2:14 7 | ╷ 8 | 2 │ let ys = {...xs}; 9 | ╵ ^~ 10 | Error: Type mismatch in unpack. Expected Dict, but got: 11 | 12 | List[Number] 13 | 14 | Help: '...' unpacks dicts, use '..' to unpack lists and sets. 15 | -------------------------------------------------------------------------------- /golden/types/expr_args_set_none.test: -------------------------------------------------------------------------------- 1 | let x: Set = {}; 2 | x 3 | 4 | # output: 5 | stdin:1:8 6 | ╷ 7 | 1 │ let x: Set = {}; 8 | ╵ ^~~ 9 | Error: Expected a concrete type, but found uninstantiated generic type. 10 | 11 | Help: 'Set' without type parameters cannot be used directly. 12 | Specify an element type, e.g. 'Set[String]'. 13 | -------------------------------------------------------------------------------- /golden/error/lex_fstring_double_string_unclosed.test: -------------------------------------------------------------------------------- 1 | f"There is no {"closing"} quote there --> 2 | 3 | # output: 4 | stdin:2:1 5 | ╷ 6 | 2 │ 7 | ╵ ^ 8 | Error: Unexpected end of input, format string is not closed. 9 | 10 | stdin:1:1 11 | ╷ 12 | 1 │ f"There is no {"closing"} quote there --> 13 | ╵ ^~ 14 | Note: Format string opened here. 15 | -------------------------------------------------------------------------------- /golden/error/std_string_parse_number_invalid_padding.test: -------------------------------------------------------------------------------- 1 | " 42 ".parse_number() 2 | 3 | # output: 4 | stdin:1:1 5 | ╷ 6 | 1 │ " 42 ".parse_number() 7 | ╵ ^~~~~~ 8 | Error: Failed to parse as number: " 42 " 9 | 10 | stdin:1:20 11 | ╷ 12 | 1 │ " 42 ".parse_number() 13 | ╵ ^ 14 | In call to method 'String.parse_number'. 15 | -------------------------------------------------------------------------------- /golden/error/std_string_remove_prefix_arg_type.test: -------------------------------------------------------------------------------- 1 | "ab".remove_prefix(0) 2 | 3 | # output: 4 | stdin:1:20 5 | ╷ 6 | 1 │ "ab".remove_prefix(0) 7 | ╵ ^ 8 | Error: Prefix must be a string. 9 | 10 | stdin:1:19 11 | ╷ 12 | 1 │ "ab".remove_prefix(0) 13 | ╵ ^ 14 | In call to method 'String.remove_prefix'. 15 | -------------------------------------------------------------------------------- /golden/error/std_string_remove_suffix_arg_type.test: -------------------------------------------------------------------------------- 1 | "ab".remove_suffix(0) 2 | 3 | # output: 4 | stdin:1:20 5 | ╷ 6 | 1 │ "ab".remove_suffix(0) 7 | ╵ ^ 8 | Error: Suffix must be a string. 9 | 10 | stdin:1:19 11 | ╷ 12 | 1 │ "ab".remove_suffix(0) 13 | ╵ ^ 14 | In call to method 'String.remove_suffix'. 15 | -------------------------------------------------------------------------------- /golden/error/std_string_starts_with_not_string.test: -------------------------------------------------------------------------------- 1 | "abc".starts_with(123) 2 | 3 | # output: 4 | stdin:1:19 5 | ╷ 6 | 1 │ "abc".starts_with(123) 7 | ╵ ^~~ 8 | Error: Prefix must be a string. 9 | 10 | stdin:1:18 11 | ╷ 12 | 1 │ "abc".starts_with(123) 13 | ╵ ^ 14 | In call to method 'String.starts_with'. 15 | -------------------------------------------------------------------------------- /golden/fmt/json_dict.test: -------------------------------------------------------------------------------- 1 | let wide = {"a" : 1, "b" : 2}; 2 | let tall = { 3 | // The comment forces tall mode. 4 | "a" 5 | : 1, 6 | "b": 7 | 2 8 | }; 9 | wide | tall 10 | 11 | # output: 12 | let wide = { "a": 1, "b": 2 }; 13 | let tall = { 14 | // The comment forces tall mode. 15 | "a": 1, 16 | "b": 2, 17 | }; 18 | wide | tall 19 | -------------------------------------------------------------------------------- /golden/json/list_index.test: -------------------------------------------------------------------------------- 1 | let xs = ["a", "b", "c", "x", "y", "z"]; 2 | [ 3 | xs[0], 4 | xs[1], 5 | xs[-6], 6 | xs[-2], 7 | xs[-1], 8 | // Indices can be numbers with decimal point, as long as they are integral. 9 | xs[0.0], 10 | xs[0.1e1], 11 | xs[20e-1], 12 | ] 13 | 14 | # output: 15 | ["a", "b", "a", "y", "z", "a", "b", "c"] 16 | -------------------------------------------------------------------------------- /golden/types/expr_args_list_none.test: -------------------------------------------------------------------------------- 1 | let x: List = []; 2 | x 3 | 4 | # output: 5 | stdin:1:8 6 | ╷ 7 | 1 │ let x: List = []; 8 | ╵ ^~~~ 9 | Error: Expected a concrete type, but found uninstantiated generic type. 10 | 11 | Help: 'List' without type parameters cannot be used directly. 12 | Specify an element type, e.g. 'List[String]'. 13 | -------------------------------------------------------------------------------- /golden/types/static_assertion_not_bool.test: -------------------------------------------------------------------------------- 1 | assert 0: "The condition should be a boolean."; 2 | 0 3 | 4 | # output: 5 | stdin:1:8 6 | ╷ 7 | 1 │ assert 0: "The condition should be a boolean."; 8 | ╵ ^ 9 | Error: Type mismatch. Expected Bool but found Number. 10 | 11 | Help: There is no implicit conversion, conditions must be boolean. 12 | -------------------------------------------------------------------------------- /golden/build/error_format_not_string.test: -------------------------------------------------------------------------------- 1 | { 2 | "out.txt": { 3 | contents = "", 4 | format = 42, 5 | }, 6 | } 7 | 8 | # output: 9 | stdin:1:1 10 | ╷ 11 | 1 │ { 12 | ╵ ^ 13 | in value 14 | at key "out.txt" 15 | at key "format" 16 | Error: Invalid output format: 42. 17 | 18 | Help: See 'rcl evaluate --help' for supported output formats. 19 | -------------------------------------------------------------------------------- /golden/error/lex_fstring_double_hole_unclosed_eof.test: -------------------------------------------------------------------------------- 1 | f"This f-string is not closed.{ 2 | 3 | # output: 4 | stdin:2:1 5 | ╷ 6 | 2 │ 7 | ╵ ^ 8 | Error: Expected '}' here to close format string hole. 9 | 10 | stdin:1:31 11 | ╷ 12 | 1 │ f"This f-string is not closed.{ 13 | ╵ ^ 14 | Note: Unmatched '{' opened here. 15 | -------------------------------------------------------------------------------- /golden/error/parse_type_annotation_fn_no_paren.test: -------------------------------------------------------------------------------- 1 | let double: Number -> Number = x => x * 2; 2 | double 3 | 4 | # output: 5 | stdin:1:20 6 | ╷ 7 | 1 │ let double: Number -> Number = x => x * 2; 8 | ╵ ^~ 9 | Error: Expected '=' after type annotation. 10 | 11 | Help: Function types require parentheses, e.g. '(String) -> Bool'. 12 | -------------------------------------------------------------------------------- /golden/error/std_range_not_int_0.test: -------------------------------------------------------------------------------- 1 | std.range("not int", 10) 2 | 3 | # output: 4 | stdin:1:11 5 | ╷ 6 | 1 │ std.range("not int", 10) 7 | ╵ ^~~~~~~~~ 8 | Error: Expected lower bound to be integer, but got "not int". 9 | 10 | stdin:1:10 11 | ╷ 12 | 1 │ std.range("not int", 10) 13 | ╵ ^ 14 | In call to function 'std.range'. 15 | -------------------------------------------------------------------------------- /golden/error/std_range_not_int_1.test: -------------------------------------------------------------------------------- 1 | std.range(0, "not int") 2 | 3 | # output: 4 | stdin:1:14 5 | ╷ 6 | 1 │ std.range(0, "not int") 7 | ╵ ^~~~~~~~~ 8 | Error: Expected upper bound to be integer, but got "not int". 9 | 10 | stdin:1:10 11 | ╷ 12 | 1 │ std.range(0, "not int") 13 | ╵ ^ 14 | In call to function 'std.range'. 15 | -------------------------------------------------------------------------------- /golden/error/typecheck_unpack_elem_invalid.test: -------------------------------------------------------------------------------- 1 | // The key-value makes the {}-literal a dict, 2 | // and inside there, .. is invalid. 3 | { a = 1, ..[] } 4 | 5 | # output: 6 | stdin:3:10 7 | ╷ 8 | 3 │ { a = 1, ..[] } 9 | ╵ ^~~~ 10 | Error: Invalid unpack in dict. 11 | 12 | Help: '..' unpacks lists and sets, use '...' to unpack dicts. 13 | -------------------------------------------------------------------------------- /golden/rcl/typed_let_primitive.test: -------------------------------------------------------------------------------- 1 | // All of the following values fit the annotated primitive types. 2 | let f0: Bool = false; 3 | let f1: Bool = not false; 4 | let i0: Number = 0; 5 | let i1: Number = 21 + 21; 6 | let i3: Number = [1, 2, 3].len(); 7 | let n: Null = null; 8 | let s0: String = "Hello, world."; 9 | null 10 | 11 | # output: 12 | null 13 | -------------------------------------------------------------------------------- /golden/rcl/typed_subtype_function_ok.test: -------------------------------------------------------------------------------- 1 | // Define a function with a particular type `(Any) -> Bool`. 2 | let f = x => x == 42; 3 | // Forget about that type. 4 | let g: Any = f; 5 | // Now we have to check at runtime that the value of `g` satisfies the type, 6 | // and it does. 7 | let h: (Number) -> Bool = g; 8 | h(21) 9 | 10 | # output: 11 | false 12 | -------------------------------------------------------------------------------- /golden/types/static_call_arity_lambda2_too_many.test: -------------------------------------------------------------------------------- 1 | // TODO: This error should point at the function definition too. 2 | let f = (x, y) => x; 3 | f("one", "too", "many") 4 | 5 | # output: 6 | stdin:3:17 7 | ╷ 8 | 3 │ f("one", "too", "many") 9 | ╵ ^~~~~~ 10 | Error: Unexpected argument. The function takes 2 arguments, but got 3. 11 | -------------------------------------------------------------------------------- /golden/types/static_union_null.test: -------------------------------------------------------------------------------- 1 | let x = null; 2 | x | std 3 | 4 | # output: 5 | stdin:2:3 6 | ╷ 7 | 2 │ x | std 8 | ╵ ^ 9 | Error: Expected Dict or Set as the left-hand side of | operator, but found this: 10 | 11 | Null 12 | 13 | stdin:1:9 14 | ╷ 15 | 1 │ let x = null; 16 | ╵ ^~~~ 17 | Note: Found Null because of this value. 18 | -------------------------------------------------------------------------------- /golden/error/runtime_fstring_not_formattable.test: -------------------------------------------------------------------------------- 1 | f"This cannot be interpolated: {{x = 1}.contains}." 2 | 3 | # output: 4 | stdin:1:33 5 | ╷ 6 | 1 │ f"This cannot be interpolated: {{x = 1}.contains}." 7 | ╵ ^~~~~~~~~~~~~~~~ 8 | Error: This value cannot be interpolated into a string: 9 | 10 | «method Dict.contains» 11 | -------------------------------------------------------------------------------- /golden/error/typecheck_unpack_elem_dict.test: -------------------------------------------------------------------------------- 1 | let kv = { a = 1 }; 2 | let ys = [..kv]; 3 | ys 4 | 5 | # output: 6 | stdin:2:13 7 | ╷ 8 | 2 │ let ys = [..kv]; 9 | ╵ ^~ 10 | Error: Type mismatch in unpack. Expected List or Set, but got: 11 | 12 | Dict[String, Number] 13 | 14 | Help: '..' unpacks lists and sets, use '...' to unpack dicts. 15 | -------------------------------------------------------------------------------- /golden/types/expr_args_union_none.test: -------------------------------------------------------------------------------- 1 | let x: Union = {}; 2 | x 3 | 4 | # output: 5 | stdin:1:8 6 | ╷ 7 | 1 │ let x: Union = {}; 8 | ╵ ^~~~~ 9 | Error: Expected a concrete type, but found uninstantiated union type. 10 | 11 | Help: 'Union' without type parameters cannot be used directly. 12 | Specify types to union, e.g. 'Union[Number, Null]'. 13 | -------------------------------------------------------------------------------- /golden/toml/top_level_dict.test: -------------------------------------------------------------------------------- 1 | { 2 | rbatty = { 3 | name = "Roy Batty", 4 | model = "Nexus-6", 5 | }, 6 | rachael = { 7 | name = "Rachael Tyrell", 8 | model = "Nexus-7", 9 | }, 10 | } 11 | 12 | # output: 13 | [rachael] 14 | model = "Nexus-7" 15 | name = "Rachael Tyrell" 16 | 17 | [rbatty] 18 | model = "Nexus-6" 19 | name = "Roy Batty" 20 | -------------------------------------------------------------------------------- /golden/types/expr_args_dict_none.test: -------------------------------------------------------------------------------- 1 | let x: Dict = {}; 2 | x 3 | 4 | # output: 5 | stdin:1:8 6 | ╷ 7 | 1 │ let x: Dict = {}; 8 | ╵ ^~~~ 9 | Error: Expected a concrete type, but found uninstantiated generic type. 10 | 11 | Help: 'Dict' without type parameters cannot be used directly. 12 | Specify a key and value type, e.g. 'Dict[String, Number]'. 13 | -------------------------------------------------------------------------------- /golden/error/runtime_overflow_add_2.test: -------------------------------------------------------------------------------- 1 | // This is a regression test for a crash caught by the fuzzer. 2 | // It used to overflow a subtraction in decimal.rs, where it subtracts 3 | // the exponents. 4 | 77E-19263+7E19238 5 | 6 | # output: 7 | stdin:4:10 8 | ╷ 9 | 4 │ 77E-19263+7E19238 10 | ╵ ^ 11 | Error: Addition 77e-19263 + 7e19238 would overflow. 12 | -------------------------------------------------------------------------------- /golden/error/typecheck_iter_arity_list.test: -------------------------------------------------------------------------------- 1 | [for k, v in ["a", "b"]: k] 2 | 3 | # output: 4 | stdin:1:6 5 | ╷ 6 | 1 │ [for k, v in ["a", "b"]: k] 7 | ╵ ^~~~ 8 | Error: Expected a single variable. 9 | 10 | stdin:1:14 11 | ╷ 12 | 1 │ [for k, v in ["a", "b"]: k] 13 | ╵ ^~~~~~~~~~ 14 | Note: This is a list, it yields one element per iteration. 15 | -------------------------------------------------------------------------------- /golden/error/typecheck_iter_arity_set.test: -------------------------------------------------------------------------------- 1 | [for k, v in {"a", "b"}: k] 2 | 3 | # output: 4 | stdin:1:6 5 | ╷ 6 | 1 │ [for k, v in {"a", "b"}: k] 7 | ╵ ^~~~ 8 | Error: Expected a single variable. 9 | 10 | stdin:1:14 11 | ╷ 12 | 1 │ [for k, v in {"a", "b"}: k] 13 | ╵ ^~~~~~~~~~ 14 | Note: This is a set, it yields one element per iteration. 15 | -------------------------------------------------------------------------------- /golden/json/fstring_triple.test: -------------------------------------------------------------------------------- 1 | [ 2 | // Two parts, no inner fragment. 3 | f""" 4 | Interpolated string: {"hello, world"} 5 | """, 6 | 7 | // Three parts, one inner fragment. 8 | f""" 9 | Begin {"hole1" 10 | } inner {"hole2"} end. 11 | """, 12 | ] 13 | 14 | # output: 15 | ["Interpolated string: hello, world\n", "Begin hole1 inner hole2 end.\n"] 16 | -------------------------------------------------------------------------------- /golden/rcl/list_all.test: -------------------------------------------------------------------------------- 1 | { 2 | a = [].all(x => x), 3 | b = [1, 2, 3].all(x => x > 2), 4 | c = [1, 2, 3].all(x => x > 0), 5 | // The assertion is never hit, we never evaluate the predicate on the third element. 6 | d = [1, 2, 3].all(x => assert x != 3: "'any' short-circuits."; x < 2), 7 | } 8 | 9 | # output: 10 | { a = true, b = false, c = true, d = false } 11 | -------------------------------------------------------------------------------- /golden/rcl/list_any.test: -------------------------------------------------------------------------------- 1 | { 2 | a = [].any(x => x), 3 | b = [1, 2, 3].any(x => x > 2), 4 | c = [1, 2, 3].any(x => x > 10), 5 | // The assertion is never hit, we never evaluate the predicate on the third element. 6 | d = [1, 2, 3].any(x => assert x < 3: "'any' short-circuits."; x > 1), 7 | } 8 | 9 | # output: 10 | { a = false, b = true, c = false, d = true } 11 | -------------------------------------------------------------------------------- /golden/types/static_call_arity_lambda2_too_few.test: -------------------------------------------------------------------------------- 1 | let f = (x, y) => x; 2 | f("too few") 3 | 4 | # output: 5 | stdin:2:12 6 | ╷ 7 | 2 │ f("too few") 8 | ╵ ^ 9 | Error: Missing argument 'y'. The function takes 2 arguments, but got 1. 10 | 11 | stdin:1:13 12 | ╷ 13 | 1 │ let f = (x, y) => x; 14 | ╵ ^ 15 | Note: Argument defined here. 16 | -------------------------------------------------------------------------------- /golden/error_raw/not_string_in_list.test: -------------------------------------------------------------------------------- 1 | ["This one is fine", {"This one is not": 1}] 2 | 3 | # output: 4 | stdin:1:1 5 | ╷ 6 | 1 │ ["This one is fine", {"This one is not": 1}] 7 | ╵ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 8 | in value 9 | at index 1 10 | Error: Expected a string for raw output, but got non-string value: { 11 | "This one is not": 1, 12 | } 13 | -------------------------------------------------------------------------------- /golden/build/error_format_invalid.test: -------------------------------------------------------------------------------- 1 | { 2 | "out.txt": { 3 | contents = "", 4 | format = "invalid-format", 5 | }, 6 | } 7 | 8 | # output: 9 | stdin:1:1 10 | ╷ 11 | 1 │ { 12 | ╵ ^ 13 | in value 14 | at key "out.txt" 15 | at key "format" 16 | Error: Invalid output format: "invalid-format". 17 | 18 | Help: See 'rcl evaluate --help' for supported output formats. 19 | -------------------------------------------------------------------------------- /golden/cmd/_input_fmt_ok.rcl: -------------------------------------------------------------------------------- 1 | // This file exists to test passing file names on the command line. Virtually 2 | // all tests pass the input on stdin, because that's how the test runner works, 3 | // but we do want to test some parts of `main`, and the loader, so we have this 4 | // file here whose primary purpose is to be referenced from other tests in this 5 | // directory. 6 | null 7 | -------------------------------------------------------------------------------- /golden/error/parse_type_annotation_unmatched_bracket.test: -------------------------------------------------------------------------------- 1 | let xs: List[Number + String] = []; 2 | xs 3 | 4 | # output: 5 | stdin:1:21 6 | ╷ 7 | 1 │ let xs: List[Number + String] = []; 8 | ╵ ^ 9 | Error: Expected ']'. 10 | 11 | stdin:1:13 12 | ╷ 13 | 1 │ let xs: List[Number + String] = []; 14 | ╵ ^ 15 | Note: Unmatched '[' opened here. 16 | -------------------------------------------------------------------------------- /golden/error/std_list_any_not_bool.test: -------------------------------------------------------------------------------- 1 | [1, 2, 3].any(x => x) 2 | 3 | # output: 4 | stdin:1:15 5 | ╷ 6 | 1 │ [1, 2, 3].any(x => x) 7 | ╵ ^~~~~~ 8 | Error: Type mismatch. Expected predicate for 'List.any' to return Bool, but it returned 1. 9 | 10 | stdin:1:14 11 | ╷ 12 | 1 │ [1, 2, 3].any(x => x) 13 | ╵ ^ 14 | In call to method 'List.any'. 15 | -------------------------------------------------------------------------------- /golden/error/std_string_remove_prefix.test: -------------------------------------------------------------------------------- 1 | "ab".remove_prefix("c") 2 | 3 | # output: 4 | stdin:1:20 5 | ╷ 6 | 1 │ "ab".remove_prefix("c") 7 | ╵ ^~~ 8 | Error: Cannot remove this prefix. "ab" does not start with "c". 9 | 10 | stdin:1:19 11 | ╷ 12 | 1 │ "ab".remove_prefix("c") 13 | ╵ ^ 14 | In call to method 'String.remove_prefix'. 15 | -------------------------------------------------------------------------------- /golden/error/std_string_remove_suffix.test: -------------------------------------------------------------------------------- 1 | "ab".remove_suffix("c") 2 | 3 | # output: 4 | stdin:1:20 5 | ╷ 6 | 1 │ "ab".remove_suffix("c") 7 | ╵ ^~~ 8 | Error: Cannot remove this suffix. "ab" does not end with "c". 9 | 10 | stdin:1:19 11 | ╷ 12 | 1 │ "ab".remove_suffix("c") 13 | ╵ ^ 14 | In call to method 'String.remove_suffix'. 15 | -------------------------------------------------------------------------------- /golden/error/type_index_inferred_mismatch.test: -------------------------------------------------------------------------------- 1 | { 1: "32" }["Not a Number"] 2 | 3 | # output: 4 | stdin:1:13 5 | ╷ 6 | 1 │ { 1: "32" }["Not a Number"] 7 | ╵ ^~~~~~~~~~~~~~ 8 | Error: Type mismatch. Expected Number but found String. 9 | 10 | stdin:1:3 11 | ╷ 12 | 1 │ { 1: "32" }["Not a Number"] 13 | ╵ ^ 14 | Note: Expected Number because of this value. 15 | -------------------------------------------------------------------------------- /golden/json/fstring_nested.test: -------------------------------------------------------------------------------- 1 | let n = 1; 2 | f"f-strings can be { 3 | let n = n + 1; 4 | f"nested arbitrarily. { 5 | let n = n + 1; 6 | f"This one is at depth {n}," 7 | } this one at depth {n}," 8 | } and this one at depth {n}." 9 | 10 | # output: 11 | "f-strings can be nested arbitrarily. This one is at depth 3, this one at depth 2, and this one at depth 1." 12 | -------------------------------------------------------------------------------- /golden/rcl/set_all.test: -------------------------------------------------------------------------------- 1 | { 2 | a = std.empty_set.all(x => x), 3 | b = {1, 2, 3}.all(x => x > 2), 4 | c = {1, 2, 3}.all(x => x > 0), 5 | // The assertion is never hit, we never evaluate the predicate on the third element. 6 | d = {1, 2, 3}.all(x => assert x != 3: "'any' short-circuits."; x < 2), 7 | } 8 | 9 | # output: 10 | { a = true, b = false, c = true, d = false } 11 | -------------------------------------------------------------------------------- /golden/rcl/set_any.test: -------------------------------------------------------------------------------- 1 | { 2 | a = std.empty_set.any(x => x), 3 | b = {1, 2, 3}.any(x => x > 2), 4 | c = {1, 2, 3}.any(x => x > 10), 5 | // The assertion is never hit, we never evaluate the predicate on the third element. 6 | d = {1, 2, 3}.any(x => assert x < 3: "'any' short-circuits."; x > 1), 7 | } 8 | 9 | # output: 10 | { a = false, b = true, c = false, d = true } 11 | -------------------------------------------------------------------------------- /golden/error/parse_type_annotation_fn_fat_arrow.test: -------------------------------------------------------------------------------- 1 | let double: Number => Number = x => x * 2; 2 | double 3 | 4 | # output: 5 | stdin:1:20 6 | ╷ 7 | 1 │ let double: Number => Number = x => x * 2; 8 | ╵ ^~ 9 | Error: Expected '=' after type annotation. 10 | 11 | Help: Function types require parentheses and use '->' instead of '=>', e.g. '(String) -> Bool'. 12 | -------------------------------------------------------------------------------- /golden/error/runtime_unknown_field_string.test: -------------------------------------------------------------------------------- 1 | "turbo encabulator".is_prefabulated 2 | 3 | # output: 4 | stdin:1:21 5 | ╷ 6 | 1 │ "turbo encabulator".is_prefabulated 7 | ╵ ^~~~~~~~~~~~~~~ 8 | Error: Unknown field. 9 | 10 | stdin:1:1 11 | ╷ 12 | 1 │ "turbo encabulator".is_prefabulated 13 | ╵ ^~~~~~~~~~~~~~~~~~~ 14 | Note: On value: "turbo encabulator" 15 | -------------------------------------------------------------------------------- /golden/error/std_read_file_utf8_not_string.test: -------------------------------------------------------------------------------- 1 | std.read_file_utf8(false) 2 | 3 | # output: 4 | stdin:1:20 5 | ╷ 6 | 1 │ std.read_file_utf8(false) 7 | ╵ ^~~~~ 8 | Error: Expected a String here, but got a different type. 9 | 10 | stdin:1:19 11 | ╷ 12 | 1 │ std.read_file_utf8(false) 13 | ╵ ^ 14 | In call to function 'std.read_file_utf8'. 15 | -------------------------------------------------------------------------------- /golden/error_json/builtin_function_span.test: -------------------------------------------------------------------------------- 1 | // This tests that we blame the error on the innermost expression, 2 | // even when it is preceded by a statement. 3 | let method = "String".len; 4 | assert method() == 6: "The length of 'String' is 6."; 5 | method 6 | 7 | # output: 8 | stdin:5:1 9 | ╷ 10 | 5 │ method 11 | ╵ ^~~~~~ 12 | Error: Methods cannot be exported as json. 13 | -------------------------------------------------------------------------------- /golden/json/string_chars.test: -------------------------------------------------------------------------------- 1 | [ 2 | "".chars(), 3 | "a".chars(), 4 | "abc".chars(), 5 | // The string "Z\u{00fc}rich" 6 | "Zürich".chars(), 7 | // The string "Zu\u{0308}rich" 8 | "Zürich".chars(), 9 | ] 10 | 11 | # output: 12 | [ 13 | [], 14 | ["a"], 15 | ["a", "b", "c"], 16 | ["Z", "ü", "r", "i", "c", "h"], 17 | ["Z", "u", "̈", "r", "i", "c", "h"] 18 | ] 19 | -------------------------------------------------------------------------------- /golden/types/runtime_index_list_string.test: -------------------------------------------------------------------------------- 1 | let xs: Any = [1, 2, 3]; 2 | // The typechecker doesn't know the type of xs, 3 | // so it doesn't know that the index should be an int, 4 | // this is a dynamic error. 5 | xs["not int"] 6 | 7 | # output: 8 | stdin:5:4 9 | ╷ 10 | 5 │ xs["not int"] 11 | ╵ ^~~~~~~~~ 12 | Error: Expected list index to be an integer, but got "not int". 13 | -------------------------------------------------------------------------------- /golden/error/lex_fstring_double_hole_unclosed.test: -------------------------------------------------------------------------------- 1 | f"This f-string is not closed.{0] 2 | 3 | # output: 4 | stdin:1:33 5 | ╷ 6 | 1 │ f"This f-string is not closed.{0] 7 | ╵ ^ 8 | Error: Expected '}'. 9 | 10 | stdin:1:31 11 | ╷ 12 | 1 │ f"This f-string is not closed.{0] 13 | ╵ ^ 14 | Note: Unmatched '{' opened here. 15 | -------------------------------------------------------------------------------- /grammar/rcl.vim/ftdetect/rcl.vim: -------------------------------------------------------------------------------- 1 | " RCL -- A reasonable configuration language. 2 | " Copyright 2023 Ruud van Asseldonk 3 | " 4 | " Licensed under the Apache License, Version 2.0 (the "License"); 5 | " you may not use this file except in compliance with the License. 6 | " A copy of the License has been included in the root of the repository. 7 | 8 | au BufRead,BufNewFile *.rcl setfiletype rcl 9 | -------------------------------------------------------------------------------- /golden/error/runtime_assertion_failure_value.test: -------------------------------------------------------------------------------- 1 | assert false: { message = "The message is not just a string.", value = 12 }; 2 | 0 3 | 4 | # output: 5 | stdin:1:8 6 | ╷ 7 | 1 │ assert false: { message = "The message is not just a string.", value = 12 }; 8 | ╵ ^~~~~ 9 | Error: Assertion failed. { 10 | message = "The message is not just a string.", 11 | value = 12, 12 | } 13 | -------------------------------------------------------------------------------- /golden/error/typecheck_collection_kv_scalar.test: -------------------------------------------------------------------------------- 1 | { 2 | "key": "value", 3 | "single", 4 | } 5 | 6 | # output: 7 | stdin:3:3 8 | ╷ 9 | 3 │ "single", 10 | ╵ ^~~~~~~~ 11 | Error: Expected key-value, not a single element. 12 | 13 | stdin:2:8 14 | ╷ 15 | 2 │ "key": "value", 16 | ╵ ^ 17 | Note: The collection is a dict and not a set, because it starts with a key-value. 18 | -------------------------------------------------------------------------------- /golden/error/typecheck_collection_scalar_kv.test: -------------------------------------------------------------------------------- 1 | { 2 | "single", 3 | "key": "value", 4 | } 5 | 6 | # output: 7 | stdin:3:8 8 | ╷ 9 | 3 │ "key": "value", 10 | ╵ ^ 11 | Error: Expected single element, not key-value. 12 | 13 | stdin:2:3 14 | ╷ 15 | 2 │ "single", 16 | ╵ ^~~~~~~~ 17 | Note: The collection is a set and not a dict, because it starts with a single value. 18 | -------------------------------------------------------------------------------- /golden/json/number_roundtrip.test: -------------------------------------------------------------------------------- 1 | // This is a regression test for cases discovered by the fuzz_decimal fuzzer. 2 | [ 3 | // The exponent here does not fit in i16 as positive number, but it does fit 4 | // as negative number because that range is one wider. 5 | 0.1e-32768, 6 | 7 | // TODO: Add a case for where we overflow the decimals:u8. 8 | ] 9 | 10 | # output: 11 | [0.1e-32768] 12 | -------------------------------------------------------------------------------- /golden/json/set_comprehension_empty.test: -------------------------------------------------------------------------------- 1 | // This is a set comprehension, which when converted to json becomes a list. 2 | // This is a regression test: an empty {}-collection literal is a dict, but 3 | // even when the collection is empty at runtime, if it is not syntactically 4 | // empty, then we should still recognize it's a set. 5 | {for x in [10, 11, 12]: if x < 10: x} 6 | 7 | # output: 8 | [] 9 | -------------------------------------------------------------------------------- /golden/error/parse_invalid_comment.test: -------------------------------------------------------------------------------- 1 | let 2 | 3 | 4 | weird1 = "this is allowed"; 5 | let 6 | 7 | // But this is not. 8 | weird2 = "oh no!"; 9 | 10 | # output: 11 | stdin:7:1 12 | ╷ 13 | 7 │ // But this is not. 14 | ╵ ^~~~~~~~~~~~~~~~~~~ 15 | Error: A comment is not allowed here. 16 | 17 | stdin:5:1 18 | ╷ 19 | 5 │ let 20 | ╵ ^ 21 | Note: Try inserting the comment above this instead. 22 | -------------------------------------------------------------------------------- /golden/error/std_list_filter_not_bool.test: -------------------------------------------------------------------------------- 1 | [1, 2, 3].filter(x => null) 2 | 3 | # output: 4 | stdin:1:18 5 | ╷ 6 | 1 │ [1, 2, 3].filter(x => null) 7 | ╵ ^~~~~~~~~ 8 | Error: Type mismatch. Expected the predicate to return Bool, but it returned null. 9 | 10 | stdin:1:17 11 | ╷ 12 | 1 │ [1, 2, 3].filter(x => null) 13 | ╵ ^ 14 | In call to method 'List.filter'. 15 | -------------------------------------------------------------------------------- /golden/error/std_list_sum_overflow.test: -------------------------------------------------------------------------------- 1 | [0x7fff_ffff_ffff_ffff, 1].sum() 2 | 3 | # output: 4 | stdin:1:28 5 | ╷ 6 | 1 │ [0x7fff_ffff_ffff_ffff, 1].sum() 7 | ╵ ^~~ 8 | Error: Addition 9223372036854775807 + 1 would overflow. 9 | 10 | stdin:1:31 11 | ╷ 12 | 1 │ [0x7fff_ffff_ffff_ffff, 1].sum() 13 | ╵ ^ 14 | In call to method 'List.sum'. 15 | -------------------------------------------------------------------------------- /golden/error/type_msg_bool.test: -------------------------------------------------------------------------------- 1 | // This test formatting of the Bool type in the error message. 2 | let x: Bool = 0; 3 | null 4 | 5 | # output: 6 | stdin:2:15 7 | ╷ 8 | 2 │ let x: Bool = 0; 9 | ╵ ^ 10 | Error: Type mismatch. Expected Bool but found Number. 11 | 12 | stdin:2:8 13 | ╷ 14 | 2 │ let x: Bool = 0; 15 | ╵ ^~~~ 16 | Note: Expected Bool because of this annotation. 17 | -------------------------------------------------------------------------------- /golden/error/type_msg_null.test: -------------------------------------------------------------------------------- 1 | // This test formatting of the Null type in the error message. 2 | let x: Null = 0; 3 | null 4 | 5 | # output: 6 | stdin:2:15 7 | ╷ 8 | 2 │ let x: Null = 0; 9 | ╵ ^ 10 | Error: Type mismatch. Expected Null but found Number. 11 | 12 | stdin:2:8 13 | ╷ 14 | 2 │ let x: Null = 0; 15 | ╵ ^~~~ 16 | Note: Expected Null because of this annotation. 17 | -------------------------------------------------------------------------------- /golden/error/std_number_round_negative.test: -------------------------------------------------------------------------------- 1 | [1.0.round(0), 1.0.round(-1)] 2 | 3 | # output: 4 | stdin:1:26 5 | ╷ 6 | 1 │ [1.0.round(0), 1.0.round(-1)] 7 | ╵ ^~ 8 | Error: Cannot round to negative decimals, decimals must be at least 0. 9 | 10 | stdin:1:25 11 | ╷ 12 | 1 │ [1.0.round(0), 1.0.round(-1)] 13 | ╵ ^ 14 | In call to method 'Number.round'. 15 | -------------------------------------------------------------------------------- /golden/error/type_index_inferred_mismatch_2.test: -------------------------------------------------------------------------------- 1 | { 1: "A", 2: "B" }["Not a Number"] 2 | 3 | # output: 4 | stdin:1:20 5 | ╷ 6 | 1 │ { 1: "A", 2: "B" }["Not a Number"] 7 | ╵ ^~~~~~~~~~~~~~ 8 | Error: Type mismatch. Expected Number but found String. 9 | 10 | stdin:1:3 11 | ╷ 12 | 1 │ { 1: "A", 2: "B" }["Not a Number"] 13 | ╵ ^ 14 | Note: Expected Number because of this value. 15 | -------------------------------------------------------------------------------- /golden/error/type_index_void.test: -------------------------------------------------------------------------------- 1 | // This is a regression test, the typechecker tried to blame an error on Any here. 2 | {}[{} | std] 3 | 4 | # output: 5 | stdin:2:4 6 | ╷ 7 | 2 │ {}[{} | std] 8 | ╵ ^~~~~~~~ 9 | Error: Expected a value of type Void, but no such values exist. 10 | 11 | stdin:2:1 12 | ╷ 13 | 2 │ {}[{} | std] 14 | ╵ ^~ 15 | Note: Expected Void because this collection is empty. 16 | -------------------------------------------------------------------------------- /golden/error/typecheck_iter_arity_record.test: -------------------------------------------------------------------------------- 1 | [for kv in {"key": "value"}: kv] 2 | 3 | # output: 4 | stdin:1:6 5 | ╷ 6 | 1 │ [for kv in {"key": "value"}: kv] 7 | ╵ ^~ 8 | Error: Expected two variables in dict iteration. 9 | 10 | stdin:1:12 11 | ╷ 12 | 1 │ [for kv in {"key": "value"}: kv] 13 | ╵ ^~~~~~~~~~~~~~~~ 14 | Note: This is a dict, it yields a key and value per iteration. 15 | -------------------------------------------------------------------------------- /golden/json/string_len.test: -------------------------------------------------------------------------------- 1 | let strings = [ 2 | "example", 3 | // The string "Z\u{00fc}rich" 4 | "Zürich", 5 | // The string "Zu\u{0308}rich" 6 | "Zürich", 7 | // The string "\u{1f574}\u{fe0e}" 8 | "🕴︎", 9 | ]; 10 | [ 11 | for s in strings: 12 | assert s.len() == s.chars().len(): ["String.len counts chars.", s, s.chars()]; 13 | s.len() 14 | ] 15 | 16 | # output: 17 | [7, 6, 7, 2] 18 | -------------------------------------------------------------------------------- /golden/error/parse_term_needs_paren.test: -------------------------------------------------------------------------------- 1 | let valid = [for x in (let ys = [1, 2, 3]; ys): x]; 2 | let invalid = [for x in let ys = [1, 2, 3]; ys: x]; 3 | invalid 4 | 5 | # output: 6 | stdin:2:25 7 | ╷ 8 | 2 │ let invalid = [for x in let ys = [1, 2, 3]; ys: x]; 9 | ╵ ^~~ 10 | Error: Expected a term here. 11 | 12 | Help: If this should be an expression, try wrapping it in parentheses. 13 | -------------------------------------------------------------------------------- /golden/error/std_string_parse_number_invalid_multi.test: -------------------------------------------------------------------------------- 1 | "multiple tokens".parse_number() 2 | 3 | # output: 4 | stdin:1:1 5 | ╷ 6 | 1 │ "multiple tokens".parse_number() 7 | ╵ ^~~~~~~~~~~~~~~~~ 8 | Error: Failed to parse as number: "multiple tokens" 9 | 10 | stdin:1:31 11 | ╷ 12 | 1 │ "multiple tokens".parse_number() 13 | ╵ ^ 14 | In call to method 'String.parse_number'. 15 | -------------------------------------------------------------------------------- /golden/error/type_let_mismatch_primitive.test: -------------------------------------------------------------------------------- 1 | let x: Number = "not a number"; 2 | x 3 | 4 | # output: 5 | stdin:1:17 6 | ╷ 7 | 1 │ let x: Number = "not a number"; 8 | ╵ ^~~~~~~~~~~~~~ 9 | Error: Type mismatch. Expected Number but found String. 10 | 11 | stdin:1:8 12 | ╷ 13 | 1 │ let x: Number = "not a number"; 14 | ╵ ^~~~~~ 15 | Note: Expected Number because of this annotation. 16 | -------------------------------------------------------------------------------- /golden/fmt/nested_let.test: -------------------------------------------------------------------------------- 1 | { 2 | outer_thing = let inner_thing = some_long_expression 3 | ( 4 | "with some", 5 | "long input" 6 | ); 7 | inner_thing, 8 | outer_second_thing = 2, 9 | } 10 | 11 | # output: 12 | { 13 | outer_thing = 14 | let inner_thing = some_long_expression("with some", "long input"); 15 | inner_thing, 16 | outer_second_thing = 2, 17 | } 18 | -------------------------------------------------------------------------------- /golden/fmt/parens_multiline.test: -------------------------------------------------------------------------------- 1 | let single = (x and y and z); 2 | let multi = ( 3 | // This comment forces the tall rather than wide form. 4 | true 5 | and x 6 | and y 7 | and z 8 | ); 9 | single and multi 10 | 11 | # output: 12 | let single = (x and y and z); 13 | let multi = ( 14 | // This comment forces the tall rather than wide form. 15 | true and x and y and z 16 | ); 17 | single and multi 18 | -------------------------------------------------------------------------------- /golden/rcl/string_parse_number.test: -------------------------------------------------------------------------------- 1 | let xs = [ 2 | // Same as for `parse_int`, those should still work. 3 | "42", 4 | "-42", 5 | 6 | // Binary prefix should work, with minus, and underscores. 7 | "0b101010", 8 | "-0b10_10_10", 9 | 10 | // Same for hexadecimal. 11 | "0x2a", 12 | "-0x_2_a_", 13 | ]; 14 | 15 | xs.map(x => x.parse_number()) 16 | 17 | # output: 18 | [42, -42, 42, -42, 42, -42] 19 | -------------------------------------------------------------------------------- /fuzz/src/lib.rs: -------------------------------------------------------------------------------- 1 | // RCL -- A reasonable configuration language. 2 | // Copyright 2024 Ruud van Asseldonk 3 | 4 | // Licensed under the Apache License, Version 2.0 (the "License"); 5 | // you may not use this file except in compliance with the License. 6 | // A copy of the License has been included in the root of the repository. 7 | 8 | pub mod builtins; 9 | pub mod random; 10 | pub mod smith; 11 | pub mod uber; 12 | -------------------------------------------------------------------------------- /golden/error/type_index_fn.test: -------------------------------------------------------------------------------- 1 | // This is a regression test, the typechecker tried to blame an error on Any here. 2 | let f = x => 0; 3 | let xs = {f: 0}; 4 | xs[x => x] 5 | 6 | # output: 7 | stdin:4:4 8 | ╷ 9 | 4 │ xs[x => x] 10 | ╵ ^~~~~~ 11 | Error: Dict does not have a key «function 0:119..125». 12 | 13 | stdin:4:1 14 | ╷ 15 | 4 │ xs[x => x] 16 | ╵ ^~ 17 | Note: On value: { «function 0:91..97»: 0 } 18 | -------------------------------------------------------------------------------- /golden/error/std_list_flat_map_not_collection.test: -------------------------------------------------------------------------------- 1 | [1, 2, 3].flat_map(x => x) 2 | 3 | # output: 4 | stdin:1:20 5 | ╷ 6 | 1 │ [1, 2, 3].flat_map(x => x) 7 | ╵ ^~~~~~ 8 | Error: Type mismatch. Expected the mapping function to return a list or set, but it returned 1. 9 | 10 | stdin:1:19 11 | ╷ 12 | 1 │ [1, 2, 3].flat_map(x => x) 13 | ╵ ^ 14 | In call to method 'List.flat_map'. 15 | -------------------------------------------------------------------------------- /golden/error/typecheck_collection_unpack_elem.test: -------------------------------------------------------------------------------- 1 | let xs = []; 2 | let ys = { ..xs, a = 1 }; 3 | ys 4 | 5 | # output: 6 | stdin:2:20 7 | ╷ 8 | 2 │ let ys = { ..xs, a = 1 }; 9 | ╵ ^ 10 | Error: Expected single element, not key-value. 11 | 12 | stdin:2:12 13 | ╷ 14 | 2 │ let ys = { ..xs, a = 1 }; 15 | ╵ ^~~~ 16 | Note: The collection is a set and not a dict, because of this unpack. 17 | -------------------------------------------------------------------------------- /golden/fmt/call_multiline.test: -------------------------------------------------------------------------------- 1 | let x1 = f( a, b , c); 2 | let x2 = f( a, b , c,); 3 | let y = f( 4 | // The first argument. 5 | a, 6 | // The second two arguments. 7 | b, c); 8 | x + y 9 | 10 | # output: 11 | let x1 = f(a, b, c); 12 | let x2 = f( 13 | a, 14 | b, 15 | c, 16 | ); 17 | let y = f( 18 | // The first argument. 19 | a, 20 | // The second two arguments. 21 | b, 22 | c, 23 | ); 24 | x + y 25 | -------------------------------------------------------------------------------- /golden/error/parse_ambiguous_unop.test: -------------------------------------------------------------------------------- 1 | not true and false 2 | 3 | # output: 4 | stdin:1:10 5 | ╷ 6 | 1 │ not true and false 7 | ╵ ^~~ 8 | Error: Parentheses are needed to clarify the precedence of this operator. 9 | 10 | stdin:1:1 11 | ╷ 12 | 1 │ not true and false 13 | ╵ ^~~ 14 | Note: Without parentheses, it is not clear whether this operator applies only to the left-hand side, or the full expression. 15 | -------------------------------------------------------------------------------- /golden/error/std_list_to_set_unique.test: -------------------------------------------------------------------------------- 1 | [0, 0].to_set_unique() 2 | 3 | # output: 4 | stdin:1:1 5 | ╷ 6 | 1 │ [0, 0].to_set_unique() 7 | ╵ ^~~~~~ 8 | Error: Expected unique elements to convert to set, but got a duplicate: 0 9 | 10 | Help: Use 'to_set_dedup' to discard duplicates. 11 | 12 | stdin:1:21 13 | ╷ 14 | 1 │ [0, 0].to_set_unique() 15 | ╵ ^ 16 | In call to method 'List.to_set_unique'. 17 | -------------------------------------------------------------------------------- /golden/rcl/literals.test: -------------------------------------------------------------------------------- 1 | [ 2 | 0, 3 | null, 4 | true, 5 | false, 6 | "this is a long list", 7 | "to trigger output that has", 8 | "a trailing comma", 9 | "unlike json", 10 | {1, 2, 3}, 11 | ] 12 | 13 | # output: 14 | [ 15 | 0, 16 | null, 17 | true, 18 | false, 19 | "this is a long list", 20 | "to trigger output that has", 21 | "a trailing comma", 22 | "unlike json", 23 | {1, 2, 3}, 24 | ] 25 | -------------------------------------------------------------------------------- /golden/types/static_collection_dict_unexpected.test: -------------------------------------------------------------------------------- 1 | let xs: Null = { a = 1 }; 2 | xs 3 | 4 | # output: 5 | stdin:1:16 6 | ╷ 7 | 1 │ let xs: Null = { a = 1 }; 8 | ╵ ^~~~~~~~~ 9 | Error: Type mismatch. Expected Null but found this type: 10 | 11 | Dict[String, Number] 12 | 13 | stdin:1:9 14 | ╷ 15 | 1 │ let xs: Null = { a = 1 }; 16 | ╵ ^~~~ 17 | Note: Expected Null because of this annotation. 18 | -------------------------------------------------------------------------------- /golden/error/parse_fstring_double_hole_unclosed.test: -------------------------------------------------------------------------------- 1 | f"This f-string is not closed.{0 0}" 2 | 3 | # output: 4 | stdin:1:34 5 | ╷ 6 | 1 │ f"This f-string is not closed.{0 0}" 7 | ╵ ^ 8 | Error: Expected '}' here to close format string hole. 9 | 10 | stdin:1:31 11 | ╷ 12 | 1 │ f"This f-string is not closed.{0 0}" 13 | ╵ ^ 14 | Note: Unmatched '{' opened here. 15 | -------------------------------------------------------------------------------- /golden/types/runtime_iter_set.test: -------------------------------------------------------------------------------- 1 | let xs: Any = {1, 2, 3}; 2 | // The typechecker cannot catch this, it's a runtime error. 3 | [for k, v in xs: false] 4 | 5 | # output: 6 | stdin:3:6 7 | ╷ 8 | 3 │ [for k, v in xs: false] 9 | ╵ ^~~~ 10 | Error: Expected a single variable. 11 | 12 | stdin:3:14 13 | ╷ 14 | 3 │ [for k, v in xs: false] 15 | ╵ ^~ 16 | Note: This is a set, it yields one element per iteration. 17 | -------------------------------------------------------------------------------- /golden/error/typecheck_collection_unpack_assoc.test: -------------------------------------------------------------------------------- 1 | let xs = {}; 2 | let ys = { ...xs, 42 }; 3 | ys 4 | 5 | # output: 6 | stdin:2:19 7 | ╷ 8 | 2 │ let ys = { ...xs, 42 }; 9 | ╵ ^~ 10 | Error: Expected key-value, not a single element. 11 | 12 | stdin:2:12 13 | ╷ 14 | 2 │ let ys = { ...xs, 42 }; 15 | ╵ ^~~~~ 16 | Note: The collection is a dict and not a set, because of the key-value unpack here. 17 | -------------------------------------------------------------------------------- /golden/rcl/builtin_function_fmt.test: -------------------------------------------------------------------------------- 1 | // This is a regression test to test that the value formatter formats built-in 2 | // functions in the same way that the CST formatter does, including line breaks. 3 | { some_really_long_key_name_that_causes_the_rest_to_wrap_if_long_enough = std.read_file_utf8 } 4 | 5 | # output: 6 | { 7 | some_really_long_key_name_that_causes_the_rest_to_wrap_if_long_enough = std 8 | .read_file_utf8, 9 | } 10 | -------------------------------------------------------------------------------- /golden/types/runtime_iter_list.test: -------------------------------------------------------------------------------- 1 | let xs: Any = [1, 2, 3]; 2 | // The typechecker cannot catch this, it's a runtime error. 3 | [for k, v in xs: false] 4 | 5 | # output: 6 | stdin:3:6 7 | ╷ 8 | 3 │ [for k, v in xs: false] 9 | ╵ ^~~~ 10 | Error: Expected a single variable. 11 | 12 | stdin:3:14 13 | ╷ 14 | 3 │ [for k, v in xs: false] 15 | ╵ ^~ 16 | Note: This is a list, it yields one element per iteration. 17 | -------------------------------------------------------------------------------- /golden/types/static_collection_set_not_scalar.test: -------------------------------------------------------------------------------- 1 | let xs: Set[Bool] = { a = 1 }; 2 | xs 3 | 4 | # output: 5 | stdin:1:25 6 | ╷ 7 | 1 │ let xs: Set[Bool] = { a = 1 }; 8 | ╵ ^ 9 | Error: Expected single element, not key-value, because the collection is a set. 10 | 11 | stdin:1:9 12 | ╷ 13 | 1 │ let xs: Set[Bool] = { a = 1 }; 14 | ╵ ^~~~~~~~~ 15 | Note: Expected Set because of this annotation. 16 | -------------------------------------------------------------------------------- /examples/buckets.rcl: -------------------------------------------------------------------------------- 1 | // See docs/tutorial.md for the origin of this example. 2 | { 3 | buckets = [ 4 | let retention_days = { hourly = 4, daily = 30, monthly = 365 }; 5 | for database in ["alpha", "bravo"]: 6 | for period, days in retention_days: 7 | { 8 | name = f"{database}-{period}", 9 | region = "eu-west", 10 | lifecycle_policy = { delete_after_seconds = days * 24 * 3600 }, 11 | } 12 | ], 13 | } 14 | -------------------------------------------------------------------------------- /golden/error/print_long_multibyte.test: -------------------------------------------------------------------------------- 1 | // This test that the truncation for long lines in errors does not slice code 2 | // points in half (which would panic). 3 | "🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎\invalid 🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎🕴︎" 4 | 5 | # output: 6 | stdin:3:205 7 | ╷ 8 | 3 │ …🕴︎🕴︎🕴︎\invalid 🕴︎🕴︎… 9 | ╵ ^~ 10 | Error: Invalid escape sequence. 11 | -------------------------------------------------------------------------------- /golden/json/integer_literals.test: -------------------------------------------------------------------------------- 1 | [ 2 | // Regular integers. 3 | 42, 4 | 0, 5 | 6 | // With numeric underscores. 7 | 1_000_000, 8 | 9 | // Hexadecimal. 10 | 0xc0ffee, 11 | 12 | // Hexadecimal with numeric underscores. 13 | 0x_c0_ff_ee, 14 | 15 | // Binary. 16 | 0b10001, 17 | 18 | // Binary with numeric underscores, 19 | 0b_0001_0001, 20 | ] 21 | 22 | # output: 23 | [42, 0, 1000000, 12648430, 12648430, 17, 17] 24 | -------------------------------------------------------------------------------- /golden/build/build_toml.test: -------------------------------------------------------------------------------- 1 | { 2 | "build_toml.test.out": { 3 | format = "toml", 4 | banner = "# Generated from build_toml.rcl.\n", 5 | contents = { 6 | some_table = { 7 | some_int = 42, 8 | some_str = "widget", 9 | }, 10 | }, 11 | } 12 | } 13 | 14 | # output: 15 | [1/1] build_toml.test.out 16 | # Generated from build_toml.rcl. 17 | 18 | [some_table] 19 | some_int = 42 20 | some_str = "widget" 21 | -------------------------------------------------------------------------------- /golden/error/std_range_not_int_1a.test: -------------------------------------------------------------------------------- 1 | // 0.0 is fine, even though it has a decimal point, it's integral. 2 | // 1.5 is not fine, it's not integral. 3 | std.range(0.0, 1.5) 4 | 5 | # output: 6 | stdin:3:16 7 | ╷ 8 | 3 │ std.range(0.0, 1.5) 9 | ╵ ^~~ 10 | Error: Expected upper bound to be integer, but got 1.5. 11 | 12 | stdin:3:10 13 | ╷ 14 | 3 │ std.range(0.0, 1.5) 15 | ╵ ^ 16 | In call to function 'std.range'. 17 | -------------------------------------------------------------------------------- /golden/types/runtime_iter_dict.test: -------------------------------------------------------------------------------- 1 | let xs: Any = { a = 1, b = 2 }; 2 | // The typechecker cannot catch this, it's a runtime error. 3 | [for x in xs: false] 4 | 5 | # output: 6 | stdin:3:6 7 | ╷ 8 | 3 │ [for x in xs: false] 9 | ╵ ^ 10 | Error: Expected two variables in dict iteration. 11 | 12 | stdin:3:11 13 | ╷ 14 | 3 │ [for x in xs: false] 15 | ╵ ^~ 16 | Note: This is a dict, it yields a key and value per iteration. 17 | -------------------------------------------------------------------------------- /golden/types/runtime_type_assert_not_bool.test: -------------------------------------------------------------------------------- 1 | let not_bool: Any = 0; 2 | assert not_bool: "We don't even get here"; 3 | null 4 | 5 | # output: 6 | stdin:2:8 7 | ╷ 8 | 2 │ assert not_bool: "We don't even get here"; 9 | ╵ ^~~~~~~~ 10 | Error: Type mismatch. Expected a value that fits this type: 11 | 12 | Bool 13 | 14 | But got this value: 15 | 16 | 0 17 | 18 | Help: There is no implicit conversion, conditions must be boolean. 19 | -------------------------------------------------------------------------------- /pyrcl/rcl.pyi: -------------------------------------------------------------------------------- 1 | # RCL -- A reasonable configuration language. 2 | # Copyright 2024 Ruud van Asseldonk 3 | 4 | # Licensed under the Apache License, Version 2.0 (the "License"); 5 | # you may not use this file except in compliance with the License. 6 | # A copy of the License has been included in the root of the repository. 7 | 8 | from typing import Any, Union 9 | 10 | def load_file(path: str) -> Any: ... 11 | def loads(src: str) -> Any: ... 12 | -------------------------------------------------------------------------------- /golden/error/type_let_mismatch_in_list.test: -------------------------------------------------------------------------------- 1 | let xs: List[Number] = ["not a number"]; 2 | xs 3 | 4 | # output: 5 | stdin:1:25 6 | ╷ 7 | 1 │ let xs: List[Number] = ["not a number"]; 8 | ╵ ^~~~~~~~~~~~~~ 9 | Error: Type mismatch. Expected Number but found String. 10 | 11 | stdin:1:14 12 | ╷ 13 | 1 │ let xs: List[Number] = ["not a number"]; 14 | ╵ ^~~~~~ 15 | Note: Expected Number because of this annotation. 16 | -------------------------------------------------------------------------------- /golden/toml/error_null.test: -------------------------------------------------------------------------------- 1 | // This test tests both that serializing `null` is an error, 2 | // and that the context of the error is reported properly. 3 | { 4 | outer = { 5 | inner = [ 6 | "first", 7 | null, 8 | "last", 9 | ] 10 | } 11 | } 12 | 13 | # output: 14 | stdin:3:1 15 | ╷ 16 | 3 │ { 17 | ╵ ^ 18 | in value 19 | at index 1 20 | at key "inner" 21 | at key "outer" 22 | Error: Null cannot be exported as TOML. 23 | -------------------------------------------------------------------------------- /golden/build/build_json.test: -------------------------------------------------------------------------------- 1 | { 2 | "build_json.test.out": { 3 | banner = null, 4 | format = "json", 5 | width = 20, 6 | contents = std.range(0, 20), 7 | }, 8 | } 9 | 10 | # output: 11 | [1/1] build_json.test.out 12 | [ 13 | 0, 14 | 1, 15 | 2, 16 | 3, 17 | 4, 18 | 5, 19 | 6, 20 | 7, 21 | 8, 22 | 9, 23 | 10, 24 | 11, 25 | 12, 26 | 13, 27 | 14, 28 | 15, 29 | 16, 30 | 17, 31 | 18, 32 | 19 33 | ] 34 | -------------------------------------------------------------------------------- /golden/error/runtime_unknown_field_record.test: -------------------------------------------------------------------------------- 1 | let widget = { 2 | name = "Turbo encabulator", 3 | marzlevanes = ["hydrocoptic"], 4 | }; 5 | widget.is_prefabulated 6 | 7 | # output: 8 | stdin:5:8 9 | ╷ 10 | 5 │ widget.is_prefabulated 11 | ╵ ^~~~~~~~~~~~~~~ 12 | Error: Unknown field. 13 | 14 | stdin:5:1 15 | ╷ 16 | 5 │ widget.is_prefabulated 17 | ╵ ^~~~~~ 18 | Note: On value: { marzlevanes = ["hydrocoptic"], name = "Turbo encabulator" } 19 | -------------------------------------------------------------------------------- /golden/toml/string.test: -------------------------------------------------------------------------------- 1 | { 2 | name = "Rachael", 3 | model = "Nexus-7", 4 | // A multi-line string should be escaped. 5 | description = 6 | """ 7 | With her Voight-Kampff test, it took over 8 | one hundred questions to determine her nature. 9 | """, 10 | } 11 | 12 | # output: 13 | description = "With her Voight-Kampff test, it took over\none hundred questions to determine her nature.\n" 14 | model = "Nexus-7" 15 | name = "Rachael" 16 | -------------------------------------------------------------------------------- /golden/error/std_number_round_too_large.test: -------------------------------------------------------------------------------- 1 | // 100 decimals is the most we support. 2 | [1e-90.round(100), 1e-90.round(101)] 3 | 4 | # output: 5 | stdin:2:32 6 | ╷ 7 | 2 │ [1e-90.round(100), 1e-90.round(101)] 8 | ╵ ^~~ 9 | Error: Number of decimals can be at most 100. 10 | 11 | stdin:2:31 12 | ╷ 13 | 2 │ [1e-90.round(100), 1e-90.round(101)] 14 | ╵ ^ 15 | In call to method 'Number.round'. 16 | -------------------------------------------------------------------------------- /golden/error/typecheck_unknown_variable_record_hint.test: -------------------------------------------------------------------------------- 1 | { 2 | user-wants-literal-text: "but used json syntax" 3 | } 4 | 5 | # output: 6 | stdin:2:3 7 | ╷ 8 | 2 │ user-wants-literal-text: "but used json syntax" 9 | ╵ ^~~~~~~~~~~~~~~~~~~~~~~ 10 | Error: Unknown variable. 11 | 12 | stdin:2:26 13 | ╷ 14 | 2 │ user-wants-literal-text: "but used json syntax" 15 | ╵ ^ 16 | Note: To use unquoted keys, replace ':' with '='. 17 | -------------------------------------------------------------------------------- /golden/error/typecheck_unpack_assoc_invalid_list_2.test: -------------------------------------------------------------------------------- 1 | // This is a regression test, previously this reported a type error 2 | // for expecting Dict but finding List, rather than complaining about 3 | // the dict unpack in the list. 4 | [1, ...[1, 2, 3]] 5 | 6 | # output: 7 | stdin:4:5 8 | ╷ 9 | 4 │ [1, ...[1, 2, 3]] 10 | ╵ ^~~~~~~~~~~~ 11 | Error: Invalid dict unpack in list. 12 | 13 | Help: '...' unpacks dicts, use '..' to unpack lists and sets. 14 | -------------------------------------------------------------------------------- /golden/error/typecheck_unpack_assoc_invalid_set_annotate.test: -------------------------------------------------------------------------------- 1 | let xs: Set[Any] = {...{}}; 2 | xs 3 | 4 | # output: 5 | stdin:1:21 6 | ╷ 7 | 1 │ let xs: Set[Any] = {...{}}; 8 | ╵ ^~~~~ 9 | Error: Invalid dict unpack in set. 10 | 11 | stdin:1:9 12 | ╷ 13 | 1 │ let xs: Set[Any] = {...{}}; 14 | ╵ ^~~~~~~~ 15 | Note: Found Set because of this annotation. 16 | 17 | Help: '...' unpacks dicts, use '..' to unpack lists and sets. 18 | -------------------------------------------------------------------------------- /golden/error/std_set_transitive_closure_not_iterable.test: -------------------------------------------------------------------------------- 1 | {0}.transitive_closure(x => x + 1) 2 | 3 | # output: 4 | stdin:1:24 5 | ╷ 6 | 1 │ {0}.transitive_closure(x => x + 1) 7 | ╵ ^~~~~~~~~~ 8 | Error: Type mismatch. Expected the expand function to return a list or set, but it returned 1. 9 | 10 | stdin:1:23 11 | ╷ 12 | 1 │ {0}.transitive_closure(x => x + 1) 13 | ╵ ^ 14 | In call to method 'Set.transitive_closure'. 15 | -------------------------------------------------------------------------------- /golden/fmt/if_else_flush.test: -------------------------------------------------------------------------------- 1 | if 2 | true 3 | : 4 | // When an if-then-else occurs at the top level, the flush break that the 5 | // formatter uses should not cause a blank line in the document. 6 | "yes yes" 7 | else: 8 | "oh no" 9 | 10 | # output: 11 | if true: 12 | // When an if-then-else occurs at the top level, the flush break that the 13 | // formatter uses should not cause a blank line in the document. 14 | "yes yes" 15 | else: 16 | "oh no" 17 | -------------------------------------------------------------------------------- /golden/error/print_long_suffix.test: -------------------------------------------------------------------------------- 1 | // This tests that error printing truncates the line if the line is really long. 2 | this is a syntax error, but a turbo_encabulator = { baseplate = "prefabulated aluminite"; casing = "surmounted, malleable logarithmic"; spurving_bearings = "aligned with the pentametric fan" }; 3 | true 4 | 5 | # output: 6 | stdin:2:6 7 | ╷ 8 | 2 │ this is a syntax error, but… 9 | ╵ ^~ 10 | Error: Unexpected content after the main expression. 11 | -------------------------------------------------------------------------------- /golden/fmt/seq_complexity.test: -------------------------------------------------------------------------------- 1 | // The seq here, despite easily fitting on one line, has a depth of 3, which 2 | // we consider too complex for a single line, so we force it to be tall. 3 | [ 4 | for x in xs: for y in ys: x * y 5 | ] 6 | 7 | # output: 8 | // The seq here, despite easily fitting on one line, has a depth of 3, which 9 | // we consider too complex for a single line, so we force it to be tall. 10 | [ 11 | for x in xs: 12 | for y in ys: 13 | x * y 14 | ] 15 | -------------------------------------------------------------------------------- /golden/rcl/dict_identifiers.test: -------------------------------------------------------------------------------- 1 | // Where possible this will use record syntax. 2 | // Where necessary, this will use json-style dicts. 3 | { 4 | "safe_ident": 0, 5 | "safe-ident": 1, 6 | "unsafe ident": 2, 7 | "null": 3, 8 | "": 0, 9 | "-does-not-start-with-safe-char": 42, 10 | } 11 | 12 | # output: 13 | { 14 | "": 0, 15 | "-does-not-start-with-safe-char": 42, 16 | "null": 3, 17 | safe-ident = 1, 18 | safe_ident = 0, 19 | "unsafe ident": 2, 20 | } 21 | -------------------------------------------------------------------------------- /golden/error/parse_integer_overflow_bin.test: -------------------------------------------------------------------------------- 1 | [ 2 | // Still okay: 3 | 0b__111111111111111111111111111111111111111111111111111111111111111, 4 | // Overflow: 5 | 0b1_000000000000000000000000000000000000000000000000000000000000000, 6 | ] 7 | 8 | # output: 9 | stdin:5:3 10 | ╷ 11 | 5 │ 0b1_000000000000000000000000000000000000000000000000000000000000000, 12 | ╵ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 13 | Error: Overflow in integer literal. 14 | -------------------------------------------------------------------------------- /grammar/tree-sitter-rcl/.gitignore: -------------------------------------------------------------------------------- 1 | # Projects files that `tree-sitter generate` generates, but I really don't care 2 | # about, because I want to minimize the amount of javascript that executes 3 | # outside of a browser please. We do need package.json for the file type 4 | # metadata and for `tree-sitter highlight` to work. 5 | /binding.gyp 6 | /bindings/node/ 7 | 8 | # Generated files that are intermediate build artifacts, and not needed to use 9 | # the grammar. 10 | /src/ 11 | -------------------------------------------------------------------------------- /golden/error/parse_dict_not_ident.test: -------------------------------------------------------------------------------- 1 | { 2 | ident = "valid", 3 | "not ident" = "invalid", 4 | } 5 | 6 | # output: 7 | stdin:3:15 8 | ╷ 9 | 3 │ "not ident" = "invalid", 10 | ╵ ^ 11 | Error: Expected '}'. 12 | 13 | stdin:1:1 14 | ╷ 15 | 1 │ { 16 | ╵ ^ 17 | Note: Unmatched '{' opened here. 18 | 19 | Help: To use 'key = value' record notation, the left-hand side must be an identifier. 20 | When that is not possible, use json-style '"key": value' instead. 21 | -------------------------------------------------------------------------------- /golden/types/static_collection_dict_not_kv.test: -------------------------------------------------------------------------------- 1 | let xs: Dict[Bool, Bool] = { "scalar" }; 2 | xs 3 | 4 | # output: 5 | stdin:1:30 6 | ╷ 7 | 1 │ let xs: Dict[Bool, Bool] = { "scalar" }; 8 | ╵ ^~~~~~~~ 9 | Error: Expected key-value, not a single element, because the collection is a dict. 10 | 11 | stdin:1:9 12 | ╷ 13 | 1 │ let xs: Dict[Bool, Bool] = { "scalar" }; 14 | ╵ ^~~~~~~~~~~~~~~~ 15 | Note: Expected Dict because of this annotation. 16 | -------------------------------------------------------------------------------- /golden/types/runtime_union_subtype_defer_err.test: -------------------------------------------------------------------------------- 1 | let x: Union[Any, Number] = "not number"; 2 | let y: Number = x; 3 | y 4 | 5 | # output: 6 | stdin:2:17 7 | ╷ 8 | 2 │ let y: Number = x; 9 | ╵ ^ 10 | Error: Type mismatch. Expected a value that fits this type: 11 | 12 | Number 13 | 14 | But got this value: 15 | 16 | "not number" 17 | 18 | stdin:2:8 19 | ╷ 20 | 2 │ let y: Number = x; 21 | ╵ ^~~~~~ 22 | Note: Expected Number because of this annotation. 23 | -------------------------------------------------------------------------------- /golden/error/parse_seq_if_ternary.test: -------------------------------------------------------------------------------- 1 | [ 2 | if true: "true" else "false" 3 | ] 4 | 5 | # output: 6 | stdin:2:19 7 | ╷ 8 | 2 │ if true: "true" else "false" 9 | ╵ ^~~~ 10 | Error: Expected ']'. 11 | 12 | stdin:1:1 13 | ╷ 14 | 1 │ [ 15 | ╵ ^ 16 | Note: Unmatched '[' opened here. 17 | 18 | Help: Inside a comprehension, 'if' controls the loop, there is no 'else' part. 19 | To use an if-else expression inside a comprehension, enclose the expression in parentheses. 20 | -------------------------------------------------------------------------------- /golden/error/std_number_round_overflow_range_pos.test: -------------------------------------------------------------------------------- 1 | // Scaling the mantissa to 19 significant digits would overflow, 2 | // 10^19 does not fit in an i64. 3 | [(1).round(18), (1).round(19)] 4 | 5 | # output: 6 | stdin:3:21 7 | ╷ 8 | 3 │ [(1).round(18), (1).round(19)] 9 | ╵ ^~~~~ 10 | Error: Overflow while rounding number. 11 | 12 | stdin:3:26 13 | ╷ 14 | 3 │ [(1).round(18), (1).round(19)] 15 | ╵ ^ 16 | In call to method 'Number.round'. 17 | -------------------------------------------------------------------------------- /golden/json/fstring.test: -------------------------------------------------------------------------------- 1 | [ 2 | // Two parts, no inner fragment. 3 | f"Interpolated string: {"hello, world"}", 4 | 5 | // Three parts, one inner fragment. 6 | f"Begin {"hole1"} inner {"hole2"} end.", 7 | 8 | // With integers, booleans, and null inside. 9 | f"Bool: {not false}, integer: {21 + 21}, null: {null}", 10 | ] 11 | 12 | # output: 13 | [ 14 | "Interpolated string: hello, world", 15 | "Begin hole1 inner hole2 end.", 16 | "Bool: true, integer: 42, null: null" 17 | ] 18 | -------------------------------------------------------------------------------- /grammar/tree-sitter-rcl/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "// 1": "This file is only here to make `tree-sitter highlight` work,", 3 | "// 2": "we don't use a JS package manager, so the file is otherwise empty.", 4 | "tree-sitter": [ 5 | { 6 | "scope": "source.rcl", 7 | "file-types": [ 8 | "rcl" 9 | ], 10 | "injection-regex": "rcl", 11 | "highlights": [ 12 | "queries/highlights.scm" 13 | ] 14 | } 15 | ], 16 | "main": "bindings/node" 17 | } 18 | -------------------------------------------------------------------------------- /grammar/zed/extension.toml: -------------------------------------------------------------------------------- 1 | # This file is generated, see build.rcl in the repository root. 2 | 3 | authors = ["Ruud van Asseldonk "] 4 | description = "Support for the RCL configuration language." 5 | id = "rcl" 6 | name = "RCL" 7 | repository = "https://github.com/rcl-lang/zed-rcl" 8 | schema_version = 1 9 | version = "0.12.0" 10 | 11 | [grammars] 12 | rcl = { commit = "9e14baa313f79d1717cf81e8e35f051c7da77073", repository = "https://github.com/rcl-lang/tree-sitter-rcl" } 13 | -------------------------------------------------------------------------------- /golden/types/meet_builtin.test: -------------------------------------------------------------------------------- 1 | // This tests the meet operation on builtins. 2 | let f: (Number, Number) -> List[Number] = (x, y) => [x, y]; 3 | let g = std.range; 4 | // This meets the type that comes from the annotation with the type that comes 5 | // from the builtin in both ways. 6 | // TODO: This only works after we can infer types through lookups, and for that 7 | // we need record types. 8 | let fns = [g, f, g]; 9 | [for f in fns: f(0, 2)] 10 | 11 | # output: 12 | [[0, 1], [0, 2], [0, 1]] 13 | -------------------------------------------------------------------------------- /golden/types/static_collection_dict_not_dict.test: -------------------------------------------------------------------------------- 1 | let xs: Dict[Number, Number] = null; 2 | null 3 | 4 | # output: 5 | stdin:1:32 6 | ╷ 7 | 1 │ let xs: Dict[Number, Number] = null; 8 | ╵ ^~~~ 9 | Error: Type mismatch. Expected this type: 10 | 11 | Dict[Number, Number] 12 | 13 | But found Null. 14 | 15 | stdin:1:9 16 | ╷ 17 | 1 │ let xs: Dict[Number, Number] = null; 18 | ╵ ^~~~~~~~~~~~~~~~~~~~ 19 | Note: Expected Dict because of this annotation. 20 | -------------------------------------------------------------------------------- /golden/error/std_read_file_utf8_not_found.test: -------------------------------------------------------------------------------- 1 | std.read_file_utf8("non_existing_path.txt") 2 | 3 | # output: 4 | stdin:1:20 5 | ╷ 6 | 1 │ std.read_file_utf8("non_existing_path.txt") 7 | ╵ ^~~~~~~~~~~~~~~~~~~~~~~ 8 | Error: Failed to access path '/WORKDIR/error/non_existing_path.txt': No such file or directory (os error 2) 9 | 10 | stdin:1:19 11 | ╷ 12 | 1 │ std.read_file_utf8("non_existing_path.txt") 13 | ╵ ^ 14 | In call to function 'std.read_file_utf8'. 15 | -------------------------------------------------------------------------------- /golden/error/type_msg_set.test: -------------------------------------------------------------------------------- 1 | // This test formatting of the Set type in the error message. 2 | let x: Set[Number] = []; 3 | null 4 | 5 | # output: 6 | stdin:2:22 7 | ╷ 8 | 2 │ let x: Set[Number] = []; 9 | ╵ ^~ 10 | Error: Type mismatch. Expected this type: 11 | 12 | Set[Number] 13 | 14 | But found this type: 15 | 16 | List[Void] 17 | 18 | stdin:2:8 19 | ╷ 20 | 2 │ let x: Set[Number] = []; 21 | ╵ ^~~~~~~~~~~ 22 | Note: Expected Set because of this annotation. 23 | -------------------------------------------------------------------------------- /golden/types/static_union_subtype_err.test: -------------------------------------------------------------------------------- 1 | let a0: Union[Null, Number] = "neither"; 2 | a0 3 | 4 | # output: 5 | stdin:1:31 6 | ╷ 7 | 1 │ let a0: Union[Null, Number] = "neither"; 8 | ╵ ^~~~~~~~~ 9 | Error: Type mismatch. Expected this type: 10 | 11 | Union[Null, Number] 12 | 13 | But found String. 14 | 15 | stdin:1:9 16 | ╷ 17 | 1 │ let a0: Union[Null, Number] = "neither"; 18 | ╵ ^~~~~~~~~~~~~~~~~~~ 19 | Note: Expected Union because of this annotation. 20 | -------------------------------------------------------------------------------- /golden/error/parse_depth.test: -------------------------------------------------------------------------------- 1 | // This expression is nested too deep, and triggers a defense against stack 2 | // overflow in the parser. Every line contains 50 parens. 3 | (((((((((((((((((((((((((((((((((((((((((((((((((( 4 | 0 5 | )))))))))))))))))))))))))))))))))))))))))))))))))) 6 | 7 | # output: 8 | stdin:3:50 9 | ╷ 10 | 3 │ (((((((((((((((((((((((((((((((((((((((((((((((((( 11 | ╵ ^ 12 | Error: Parser recursion limit reached, please reduce nesting. 13 | -------------------------------------------------------------------------------- /golden/toml/top_level_list_of_dict.test: -------------------------------------------------------------------------------- 1 | { 2 | users = [ 3 | { 4 | username = "rbatty", 5 | name = "Roy Batty", 6 | model = "Nexus-6", 7 | }, 8 | { 9 | username = "rachael", 10 | name = "Rachael Tyrell", 11 | model = "Nexus-7", 12 | }, 13 | ], 14 | } 15 | 16 | # output: 17 | [[users]] 18 | model = "Nexus-6" 19 | name = "Roy Batty" 20 | username = "rbatty" 21 | 22 | [[users]] 23 | model = "Nexus-7" 24 | name = "Rachael Tyrell" 25 | username = "rachael" 26 | -------------------------------------------------------------------------------- /fuzz/dictionary_base.txt: -------------------------------------------------------------------------------- 1 | # Operators 2 | "*" 3 | "+" 4 | "-" 5 | "/" 6 | "=>" 7 | "==" 8 | ">=" 9 | "<=" 10 | ">" 11 | "<" 12 | ".." 13 | "..." 14 | 15 | # Collection literals. 16 | "[]" 17 | "{}" 18 | 19 | # String literals and escape sequences. 20 | "\"\"\"" 21 | "\\\"" 22 | "\\\\" 23 | "\\u{1f574}" 24 | "f\"" 25 | "f\"\"\"" 26 | 27 | # Some examples of numbers. 28 | "0x1" 29 | "0b1" 30 | "0.1e1" 31 | # The maximum value of an i64, to make the fuzzer more likely to hit overflows. 32 | "0x7fffffffffffffff" 33 | -------------------------------------------------------------------------------- /golden/error/print_long_prefix.test: -------------------------------------------------------------------------------- 1 | // This tests that error printing truncates the line if the line is really long. 2 | let turbo_encabulator = { baseplate = "prefabulated aluminite", casing = "surmounted, malleable logarithmic", spurving_bearings = "aligned with the pentametric fan", err_unknown_variable = this-causes-an-error }; 3 | true 4 | 5 | # output: 6 | stdin:2:190 7 | ╷ 8 | 2 │ …_unknown_variable = this-causes-an-error }; 9 | ╵ ^~~~~~~~~~~~~~~~~~~~ 10 | Error: Unknown variable. 11 | -------------------------------------------------------------------------------- /golden/error/runtime_import_sandbox_deny.test: -------------------------------------------------------------------------------- 1 | // Importing files outside the working directory is not allowed by default due 2 | // to the workdir sandbox policy. 3 | import "../rcl/_import.rcl" 4 | 5 | # output: 6 | stdin:3:8 7 | ╷ 8 | 3 │ import "../rcl/_import.rcl" 9 | ╵ ^~~~~~~~~~~~~~~~~~~~ 10 | Error: Sandbox policy 'workdir' does not allow loading '/WORKDIR/rcl/_import.rcl' because it lies outside of '/WORKDIR/error'. 11 | 12 | Help: Try executing from '/WORKDIR' or use '--sandbox=unrestricted'. 13 | -------------------------------------------------------------------------------- /grammar/zed/languages/rcl/indents.scm: -------------------------------------------------------------------------------- 1 | (_ "{" "}" @end) @indent 2 | (_ "[" "]" @end) @indent 3 | (_ "(" ")" @end) @indent 4 | 5 | ; If you create a newline inside a let, that indents, we want to hang the 6 | ; body. It already indents if you enter before the `=`, but that's okay. 7 | (stmt_let) @indent 8 | 9 | ; In addition to these, there are indent regexes in `config.toml` it seems 10 | ; that both are necessary to make it work, I don't understand why. 11 | (expr_if) @start.if 12 | (expr_if "else" @start.else) 13 | -------------------------------------------------------------------------------- /golden/fmt/binop.test: -------------------------------------------------------------------------------- 1 | let wide = "a" + "b"; 2 | let tall = "supercalifragilisticexpialidocious umbrella " + 3 | "supercalifragilisticexpialidocious umbrella " + 4 | "supercalifragilisticexpialidocious umbrella " ; 5 | 6 | wide + tall + null 7 | 8 | # output: 9 | let wide = "a" + "b"; 10 | let tall = 11 | "supercalifragilisticexpialidocious umbrella " 12 | + "supercalifragilisticexpialidocious umbrella " 13 | + "supercalifragilisticexpialidocious umbrella "; 14 | 15 | wide + tall + null 16 | -------------------------------------------------------------------------------- /golden/json/strings.test: -------------------------------------------------------------------------------- 1 | [ 2 | """ 3 | 🕴︎︎ 4 | """, 5 | """ 6 | This string has a shared indent to be stripped, 7 | even though the next line has no spaces on it: 8 | 9 | """, 10 | " 11 | This string starts with a newline that should not be eaten. 12 | " 13 | ] 14 | 15 | # output: 16 | [ 17 | "🕴︎︎\n", 18 | "This string has a shared indent to be stripped,\neven though the next line has no spaces on it:\n\n", 19 | "\n This string starts with a newline that should not be eaten.\n " 20 | ] 21 | -------------------------------------------------------------------------------- /examples/build.rcl: -------------------------------------------------------------------------------- 1 | // This example is meant to be used with `rcl build`, see `rcl build --help`. 2 | // It writes formatted examples into the `out` directory. 3 | { 4 | "out/github_actions.yml": { 5 | contents = import "github_actions.rcl", 6 | format = "json", 7 | banner = "# This file is generated from github_actions.rcl.", 8 | }, 9 | "out/buckets.toml": { 10 | contents = import "buckets.rcl", 11 | format = "toml", 12 | banner = "# This file is generated from buckets.rcl.", 13 | }, 14 | } 15 | -------------------------------------------------------------------------------- /golden/error/std_list_fold_not_callable.test: -------------------------------------------------------------------------------- 1 | [1].fold(0, "not callable") 2 | 3 | # output: 4 | stdin:1:13 5 | ╷ 6 | 1 │ [1].fold(0, "not callable") 7 | ╵ ^~~~~~~~~~~~~~ 8 | Error: This is not a function, it cannot be called. 9 | 10 | stdin:1:13 11 | ╷ 12 | 1 │ [1].fold(0, "not callable") 13 | ╵ ^~~~~~~~~~~~~~ 14 | In internal call to reduce function from 'List.fold'. 15 | 16 | stdin:1:9 17 | ╷ 18 | 1 │ [1].fold(0, "not callable") 19 | ╵ ^ 20 | In call to method 'List.fold'. 21 | -------------------------------------------------------------------------------- /golden/rcl/typed_let_collections.test: -------------------------------------------------------------------------------- 1 | // All of the following values fit the annotated collection types. 2 | let d0: Dict[String, Number] = { a = 1, b = 2 }; 3 | let d1: Dict[Number, String] = { 1: "a", 2: "b" }; 4 | let l0: List[Number] = [d0.len(), d1.len()]; 5 | let l1: List[String] = ["hello", "world"]; 6 | let l2: List[Dict[String, Number]] = [{a = 1}, {b = 2}]; 7 | let s0: Set[Bool] = { true, false }; 8 | let s1: Set[String] = d0.keys(); 9 | let s2: Set[Number] = d1.keys(); 10 | null 11 | 12 | # output: 13 | null 14 | -------------------------------------------------------------------------------- /golden/error/std_set_transitive_closure_depth_function.test: -------------------------------------------------------------------------------- 1 | // See also the other transitive closure depth test. 2 | {0}.transitive_closure(x => [_ => x]) 3 | 4 | # output: 5 | stdin:2:5 6 | ╷ 7 | 2 │ {0}.transitive_closure(x => [_ => x]) 8 | ╵ ^~~~~~~~~~~~~~~~~~ 9 | Error: A value in the transitive closure exceeds the maximum nesting depth: «function 0:82..88» 10 | 11 | stdin:2:23 12 | ╷ 13 | 2 │ {0}.transitive_closure(x => [_ => x]) 14 | ╵ ^ 15 | In call to method 'Set.transitive_closure'. 16 | -------------------------------------------------------------------------------- /golden/error/typecheck_unpack_assoc_invalid_set_infer.test: -------------------------------------------------------------------------------- 1 | // The first element makes this {}-literal a set rather than a dict, 2 | // and inside a set, ...-unpack is invalid. 3 | {1, ...{}} 4 | 5 | # output: 6 | stdin:3:5 7 | ╷ 8 | 3 │ {1, ...{}} 9 | ╵ ^~~~~ 10 | Error: Invalid dict unpack in set. 11 | 12 | stdin:3:2 13 | ╷ 14 | 3 │ {1, ...{}} 15 | ╵ ^ 16 | Note: The collection is a set and not a dict, because it starts with a single value. 17 | 18 | Help: '...' unpacks dicts, use '..' to unpack lists and sets. 19 | -------------------------------------------------------------------------------- /golden/error/typecheck_unpack_elem_runtime.test: -------------------------------------------------------------------------------- 1 | let xs: Any = [1, 2, 3]; 2 | let ys: List[Bool] = [..xs]; 3 | ys 4 | 5 | # output: 6 | stdin:2:25 7 | ╷ 8 | 2 │ let ys: List[Bool] = [..xs]; 9 | ╵ ^~ 10 | in value 11 | at index 0 12 | Error: Type mismatch. Expected a value that fits this type: 13 | 14 | Bool 15 | 16 | But got this value: 17 | 18 | 1 19 | 20 | stdin:2:14 21 | ╷ 22 | 2 │ let ys: List[Bool] = [..xs]; 23 | ╵ ^~~~ 24 | Note: Expected Bool because of this annotation. 25 | -------------------------------------------------------------------------------- /golden/fmt/statement_complexity.test: -------------------------------------------------------------------------------- 1 | // This is still okay, one statement. 2 | let okay = let x = 1; 1 + x; 3 | 4 | // This, despite fitting on one line, wraps because it has two statements. 5 | let wrap = let x = 1; let y = 2; 1 + x + y; 6 | 7 | okay + wrap 8 | 9 | # output: 10 | // This is still okay, one statement. 11 | let okay = let x = 1; 1 + x; 12 | 13 | // This, despite fitting on one line, wraps because it has two statements. 14 | let wrap = 15 | let x = 1; 16 | let y = 2; 17 | 1 + x + y; 18 | 19 | okay + wrap 20 | -------------------------------------------------------------------------------- /golden/rcl/list_key_by.test: -------------------------------------------------------------------------------- 1 | let foods = [ 2 | { category = "fruit", name = "apple" }, 3 | { category = "fruit", name = "pear" }, 4 | { category = "vegetable", name = "onion" }, 5 | { category = "vegetable", name = "carrot" }, 6 | ]; 7 | foods.key_by(food => food.name) 8 | 9 | # output: 10 | { 11 | apple = { category = "fruit", name = "apple" }, 12 | carrot = { category = "vegetable", name = "carrot" }, 13 | onion = { category = "vegetable", name = "onion" }, 14 | pear = { category = "fruit", name = "pear" }, 15 | } 16 | -------------------------------------------------------------------------------- /golden/rcl/set_key_by.test: -------------------------------------------------------------------------------- 1 | let foods = { 2 | { category = "fruit", name = "apple" }, 3 | { category = "fruit", name = "pear" }, 4 | { category = "vegetable", name = "onion" }, 5 | { category = "vegetable", name = "carrot" }, 6 | }; 7 | foods.key_by(food => food.name) 8 | 9 | # output: 10 | { 11 | apple = { category = "fruit", name = "apple" }, 12 | carrot = { category = "vegetable", name = "carrot" }, 13 | onion = { category = "vegetable", name = "onion" }, 14 | pear = { category = "fruit", name = "pear" }, 15 | } 16 | -------------------------------------------------------------------------------- /golden/build/build_yaml.test: -------------------------------------------------------------------------------- 1 | { 2 | "build_yaml.test.out": { 3 | format = "yaml-stream", 4 | banner = "# Generated from build_yaml.rcl.", 5 | contents = [ 6 | for i, name in ["foo", "bar", "baz"].enumerate(): 7 | { 8 | name = name, 9 | rank = i, 10 | }, 11 | ], 12 | } 13 | } 14 | 15 | # output: 16 | [1/1] build_yaml.test.out 17 | # Generated from build_yaml.rcl. 18 | --- 19 | {"name": "foo", "rank": 0} 20 | --- 21 | {"name": "bar", "rank": 1} 22 | --- 23 | {"name": "baz", "rank": 2} 24 | -------------------------------------------------------------------------------- /golden/error/runtime_assertion_with_newline.test: -------------------------------------------------------------------------------- 1 | // A line break is not allowed in document fragments for the pretty-printer, 2 | // and printing assertion messages uses the pretty-printer, so it should break 3 | // the string into lines and not crash. This is a regression test. 4 | assert false: "This message\nhas a line break in it."; 5 | 0 6 | 7 | # output: 8 | stdin:4:8 9 | ╷ 10 | 4 │ assert false: "This message\nhas a line break in it."; 11 | ╵ ^~~~~ 12 | Error: Assertion failed. This message 13 | has a line break in it. 14 | -------------------------------------------------------------------------------- /golden/json/comparisions.test: -------------------------------------------------------------------------------- 1 | { 2 | // Regular integer comparison, as you expect. 3 | int = [0 < 1, 0 <= 1, 0 > 1, 0 >= 1], 4 | // Asciibetic (memcpm) order. 5 | str = ["a" < "b", "a" <= "b", "a" > "b", "a" >= "b"], 6 | // Lexicographic order on the elements. 7 | list_int = [[0, 1] < [0, 2], [0, 1] < [1, 0], [0, 1] <= [1], [0, 0] > [1, 0], [0] >= [0, 0]], 8 | } 9 | 10 | # output: 11 | { 12 | "int": [true, true, false, false], 13 | "list_int": [true, true, true, false, false], 14 | "str": [true, true, false, false] 15 | } 16 | -------------------------------------------------------------------------------- /golden/rcl/types_subtype_collection_ok.test: -------------------------------------------------------------------------------- 1 | let xs: List[Number] = [0, 1, 2]; 2 | // Number is a subtype of Any. This is ok. 3 | let ys: List[Any] = xs; 4 | // Any is not a subtype of Number, this causes a Defer in the check. 5 | let zs: List[Number] = ys; 6 | 7 | let xs: Set[Number] = {0, 1, 2}; 8 | let ys: Set[Any] = xs; 9 | let zs: Set[Number] = ys; 10 | 11 | let xs: Dict[String, Number] = { a = 1, b = 2 }; 12 | let ys: Dict[String, Any] = xs; 13 | let zs: Dict[String, Number] = ys; 14 | 15 | zs 16 | 17 | # output: 18 | { a = 1, b = 2 } 19 | -------------------------------------------------------------------------------- /golden/json/std_format_json.test: -------------------------------------------------------------------------------- 1 | [ 2 | std.format_json(null), 3 | std.format_json("null"), 4 | std.format_json({a = 1, b = 2}), 5 | // A long value that exceeds 80 columns if we were printing with a width, 6 | // but here it gets formatted in wide mode, not tall mode. 7 | std.format_json(std.range(0, 35)), 8 | ] 9 | 10 | # output: 11 | [ 12 | "null", 13 | "\"null\"", 14 | "{\"a\": 1, \"b\": 2}", 15 | "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34]" 16 | ] 17 | -------------------------------------------------------------------------------- /golden/json/dict_values_strings.test: -------------------------------------------------------------------------------- 1 | let dict = { 2 | name = "Rachael", 3 | model = "NEXUS-7 N7FAA52318", 4 | description = 5 | """ 6 | In the Voight-Kampff test, it took over a hundred questions 7 | to establish Rachael’s true nature. 8 | """, 9 | }; 10 | 11 | // TODO: Should be made to preserve insertion order. 12 | [ for _key, value in dict: value ] 13 | 14 | # output: 15 | [ 16 | "In the Voight-Kampff test, it took over a hundred questions\nto establish Rachael’s true nature.\n", 17 | "NEXUS-7 N7FAA52318", 18 | "Rachael" 19 | ] 20 | -------------------------------------------------------------------------------- /golden/types/mismatch_short_long.test: -------------------------------------------------------------------------------- 1 | // This tests the pretty-printer of type errors for the case where the expected 2 | // type can be printed in short form, but the actual type is long. 3 | let x: Number = [{[0]}]; 4 | x 5 | 6 | # output: 7 | stdin:3:17 8 | ╷ 9 | 3 │ let x: Number = [{[0]}]; 10 | ╵ ^~~~~~~ 11 | Error: Type mismatch. Expected Number but found this type: 12 | 13 | List[Set[List[Number]]] 14 | 15 | stdin:3:8 16 | ╷ 17 | 3 │ let x: Number = [{[0]}]; 18 | ╵ ^~~~~~ 19 | Note: Expected Number because of this annotation. 20 | -------------------------------------------------------------------------------- /golden/rcl/list_group_by.test: -------------------------------------------------------------------------------- 1 | let foods = [ 2 | { category = "fruit", name = "apple" }, 3 | { category = "fruit", name = "pear" }, 4 | { category = "vegetable", name = "onion" }, 5 | { category = "vegetable", name = "carrot" }, 6 | ]; 7 | foods.group_by(food => food.category) 8 | 9 | # output: 10 | { 11 | fruit = [ 12 | { category = "fruit", name = "apple" }, 13 | { category = "fruit", name = "pear" }, 14 | ], 15 | vegetable = [ 16 | { category = "vegetable", name = "onion" }, 17 | { category = "vegetable", name = "carrot" }, 18 | ], 19 | } 20 | -------------------------------------------------------------------------------- /golden/rcl/set_group_by.test: -------------------------------------------------------------------------------- 1 | let foods = { 2 | { category = "fruit", name = "apple" }, 3 | { category = "fruit", name = "pear" }, 4 | { category = "vegetable", name = "onion" }, 5 | { category = "vegetable", name = "carrot" }, 6 | }; 7 | foods.group_by(food => food.category) 8 | 9 | # output: 10 | { 11 | fruit = { 12 | { category = "fruit", name = "apple" }, 13 | { category = "fruit", name = "pear" }, 14 | }, 15 | vegetable = { 16 | { category = "vegetable", name = "carrot" }, 17 | { category = "vegetable", name = "onion" }, 18 | }, 19 | } 20 | -------------------------------------------------------------------------------- /golden/types/runtime_function_arity.test: -------------------------------------------------------------------------------- 1 | // Define a function of arity 2. 2 | let f = (x, y) => false; 3 | 4 | // Forget the type. 5 | let g: Any = f; 6 | 7 | // Force a runtime type check that `g` satisfies a function type of arity 1. 8 | // This should fail. 9 | let h: (Number) -> Bool = g; 10 | 11 | null 12 | 13 | # output: 14 | stdin:9:27 15 | ╷ 16 | 9 │ let h: (Number) -> Bool = g; 17 | ╵ ^ 18 | Error: Type mismatch. Expected this type: 19 | 20 | (Number) -> Bool 21 | 22 | But found this type: 23 | 24 | (x: Any, y: Any) -> Bool 25 | -------------------------------------------------------------------------------- /golden/types/runtime_type_annotation_mismatch.test: -------------------------------------------------------------------------------- 1 | // Without the Any, the typechecker would catch it statically. But now we have 2 | // to catch it at runtime. 3 | let x: Any = 32; 4 | let y: String = x; 5 | y 6 | 7 | # output: 8 | stdin:4:17 9 | ╷ 10 | 4 │ let y: String = x; 11 | ╵ ^ 12 | Error: Type mismatch. Expected a value that fits this type: 13 | 14 | String 15 | 16 | But got this value: 17 | 18 | 32 19 | 20 | stdin:4:8 21 | ╷ 22 | 4 │ let y: String = x; 23 | ╵ ^~~~~~ 24 | Note: Expected String because of this annotation. 25 | -------------------------------------------------------------------------------- /golden/error/runtime_std_range_too_big.test: -------------------------------------------------------------------------------- 1 | // We don't test the greatest range that does not fail, to keep the tests fast. 2 | let still_ok = std.range(0, 100); 3 | let err = std.range(10, 1_000_011); 4 | 0 5 | 6 | # output: 7 | stdin:3:21 8 | ╷ 9 | 3 │ let err = std.range(10, 1_000_011); 10 | ╵ ^~~~~~~~~~~~~ 11 | Error: Range 10..1000011 exceeds the maximum length of 1000000. The list would require too much memory. 12 | 13 | stdin:3:20 14 | ╷ 15 | 3 │ let err = std.range(10, 1_000_011); 16 | ╵ ^ 17 | In call to function 'std.range'. 18 | -------------------------------------------------------------------------------- /golden/error/typecheck_unpack_assoc_runtime.test: -------------------------------------------------------------------------------- 1 | let xs: Any = { a = 1, b = 2 }; 2 | let ys: Dict[String, Bool] = {...xs}; 3 | ys 4 | 5 | # output: 6 | stdin:2:34 7 | ╷ 8 | 2 │ let ys: Dict[String, Bool] = {...xs}; 9 | ╵ ^~ 10 | in value 11 | at key "a" 12 | Error: Type mismatch. Expected a value that fits this type: 13 | 14 | Bool 15 | 16 | But got this value: 17 | 18 | 1 19 | 20 | stdin:2:22 21 | ╷ 22 | 2 │ let ys: Dict[String, Bool] = {...xs}; 23 | ╵ ^~~~ 24 | Note: Expected Bool because of this annotation. 25 | -------------------------------------------------------------------------------- /golden/types/runtime_function_arg_defer.test: -------------------------------------------------------------------------------- 1 | // This tests the `FunctionArg::is_subtype_of` check for the case where the 2 | // inner args return `Defer`. This happens when we check if `Any` is a subtype 3 | // of `Number` for example. 4 | let f: (Number) -> Bool = x => true; 5 | let g: Any = f; 6 | let h: (Any) -> Bool = g; 7 | null 8 | 9 | # output: 10 | stdin:6:24 11 | ╷ 12 | 6 │ let h: (Any) -> Bool = g; 13 | ╵ ^ 14 | Error: Type mismatch. Expected this type: 15 | 16 | (Any) -> Bool 17 | 18 | But found this type: 19 | 20 | (x: Number) -> Bool 21 | -------------------------------------------------------------------------------- /grammar/tree-sitter-rcl/bindings/rust/build.rs: -------------------------------------------------------------------------------- 1 | fn main() { 2 | let src_dir = std::path::Path::new("src"); 3 | let mut c_config = cc::Build::new(); 4 | c_config.include(src_dir); 5 | c_config 6 | .flag_if_supported("-Wno-unused-parameter") 7 | .flag_if_supported("-Wno-unused-but-set-variable") 8 | .flag_if_supported("-Wno-trigraphs"); 9 | let parser_path = src_dir.join("parser.c"); 10 | c_config.file(&parser_path); 11 | c_config.compile("parser"); 12 | println!("cargo:rerun-if-changed={}", parser_path.to_str().unwrap()); 13 | } 14 | -------------------------------------------------------------------------------- /golden/fmt/seq_unpack.test: -------------------------------------------------------------------------------- 1 | let long_unpack = { .. widgets.properties_a, .. widgets.properties_b,..widgets.properties_c}; 2 | let short_unpack = [ ..xs, .. ys ]; 3 | let dict_unpack = { k = v, ... default_options, ... additional_options, for p, q in ys: ...q }; 4 | null 5 | 6 | # output: 7 | let long_unpack = { 8 | ..widgets.properties_a, 9 | ..widgets.properties_b, 10 | ..widgets.properties_c, 11 | }; 12 | let short_unpack = [..xs, ..ys]; 13 | let dict_unpack = { 14 | k = v, 15 | ...default_options, 16 | ...additional_options, 17 | for p, q in ys: ...q, 18 | }; 19 | null 20 | -------------------------------------------------------------------------------- /golden/fmt/types_blank_in_annotation.test: -------------------------------------------------------------------------------- 1 | // This is a regression test for a non-idempotency discovered by the fuzzer. 2 | // The blank line should be removed entirely, but previously it turned into a 3 | // single newline, which then got removed on a second fmt iteration. 4 | let x: 5 | 6 | T = q; 7 | 0 8 | 9 | # output: 10 | // This is a regression test for a non-idempotency discovered by the fuzzer. 11 | // The blank line should be removed entirely, but previously it turned into a 12 | // single newline, which then got removed on a second fmt iteration. 13 | let x: T = q; 14 | 0 15 | -------------------------------------------------------------------------------- /grammar/tree-sitter-rcl/queries/highlights.scm: -------------------------------------------------------------------------------- 1 | ["for" "in"] @keyword 2 | ["assert" "trace"] @keyword 3 | ["if" "else"] @keyword 4 | ["import"] @keyword 5 | ["let"] @keyword 6 | (unop_keyword) @keyword 7 | (binop_keyword) @keyword 8 | 9 | (comment) @comment 10 | (shebang) @keyword 11 | (number) @number 12 | (bool) @constant 13 | (null) @constant 14 | 15 | (string) @string 16 | (string_escape) @string.special 17 | (string_hole (["{" "}"] @string.special)) 18 | 19 | (seq_assoc_expr field: (string) @property) 20 | (seq_assoc_ident field: (ident) @property) 21 | 22 | (type_term) @type 23 | -------------------------------------------------------------------------------- /golden/error/std_list_group_by_not_callable.test: -------------------------------------------------------------------------------- 1 | [1, 2, 3].group_by("not a function") 2 | 3 | # output: 4 | stdin:1:20 5 | ╷ 6 | 1 │ [1, 2, 3].group_by("not a function") 7 | ╵ ^~~~~~~~~~~~~~~~ 8 | Error: This is not a function, it cannot be called. 9 | 10 | stdin:1:20 11 | ╷ 12 | 1 │ [1, 2, 3].group_by("not a function") 13 | ╵ ^~~~~~~~~~~~~~~~ 14 | In internal call to key selector from 'List.group_by'. 15 | 16 | stdin:1:19 17 | ╷ 18 | 1 │ [1, 2, 3].group_by("not a function") 19 | ╵ ^ 20 | In call to method 'List.group_by'. 21 | -------------------------------------------------------------------------------- /golden/json_lines/list.test: -------------------------------------------------------------------------------- 1 | [ 2 | // A list that would normally be formatted tall, but now wide. 3 | std.range(0, 35), 4 | { a = 1, b = true, c = std.range(0, 15) }, 5 | null, 6 | """ 7 | This multi-line string 8 | goes on a single line in the result. 9 | """, 10 | ] 11 | 12 | # output: 13 | [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34] 14 | {"a": 1, "b": true, "c": [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14]} 15 | null 16 | "This multi-line string\ngoes on a single line in the result.\n" 17 | -------------------------------------------------------------------------------- /golden/toml/keys_quoted.test: -------------------------------------------------------------------------------- 1 | // This tests that keys get quoted when necessary. 2 | { 3 | "042": "Seemingly integer key", 4 | _underscores_are_ok = true, 5 | dashes-are-ok = true, 6 | "-start-dashes-too": "of course", 7 | "spaces are ok": false, 8 | "schreibökonomisch": "certainly not", 9 | "": "allowed but discouraged", 10 | } 11 | 12 | # output: 13 | "" = "allowed but discouraged" 14 | -start-dashes-too = "of course" 15 | 042 = "Seemingly integer key" 16 | _underscores_are_ok = true 17 | dashes-are-ok = true 18 | "schreibökonomisch" = "certainly not" 19 | "spaces are ok" = false 20 | -------------------------------------------------------------------------------- /fuzz/dictionary_cli.txt: -------------------------------------------------------------------------------- 1 | # Commands 2 | "build" 3 | "evaluate" 4 | "fmt" 5 | "format" 6 | "highlight" 7 | "patch" 8 | "query" 9 | 10 | # Shorthands 11 | "je" 12 | "jq" 13 | "re" 14 | "rq" 15 | 16 | # Options 17 | "--about" 18 | "--banner" 19 | "--check" 20 | "--color" 21 | "--directory" 22 | "--dry-run" 23 | "--format" 24 | "--help" 25 | "--in-place" 26 | "--output-depfile" 27 | "--sandbox" 28 | "--version" 29 | "--width" 30 | 31 | # Option arguments 32 | "ansi" 33 | "auto" 34 | "html" 35 | "json" 36 | "json-lines" 37 | "none" 38 | "rcl" 39 | "toml" 40 | "unrestricted" 41 | "workdir" 42 | "yaml-stream" 43 | -------------------------------------------------------------------------------- /golden/error/std_list_fold_depth_method.test: -------------------------------------------------------------------------------- 1 | // The depth increases, because the List.len method retains its receiver. 2 | std.range(0, 50).fold([], (acc, x) => [null, true, acc.len]) 3 | 4 | # output: 5 | stdin:2:18 6 | ╷ 7 | 2 │ std.range(0, 50).fold([], (acc, x) => [null, true, acc.len]) 8 | ╵ ^~~~ 9 | Error: Accumulator exceeds the maximum nesting depth: [ 10 | null, 11 | true, 12 | «method List.len», 13 | ] 14 | 15 | stdin:2:22 16 | ╷ 17 | 2 │ std.range(0, 50).fold([], (acc, x) => [null, true, acc.len]) 18 | ╵ ^ 19 | In call to method 'List.fold'. 20 | -------------------------------------------------------------------------------- /golden/error/std_string_parse_number_overflow.test: -------------------------------------------------------------------------------- 1 | // This is i64::MAX, we should be able to parse it. 2 | let x = "9223372036854775807".parse_number(); 3 | 4 | // This is one more than i64::MAX, which we can't represent. 5 | "9223372036854775808".parse_number() 6 | 7 | # output: 8 | stdin:5:1 9 | ╷ 10 | 5 │ "9223372036854775808".parse_number() 11 | ╵ ^~~~~~~~~~~~~~~~~~~~~ 12 | Error: Overflow while parsing number: "9223372036854775808" 13 | 14 | stdin:5:35 15 | ╷ 16 | 5 │ "9223372036854775808".parse_number() 17 | ╵ ^ 18 | In call to method 'String.parse_number'. 19 | -------------------------------------------------------------------------------- /golden/error/type_in_unpack_element.test: -------------------------------------------------------------------------------- 1 | let bools = [true, false]; 2 | let ys: List[Number] = [..bools]; 3 | ys 4 | 5 | # output: 6 | stdin:2:25 7 | ╷ 8 | 2 │ let ys: List[Number] = [..bools]; 9 | ╵ ^~~~~~~ 10 | Error: Type mismatch in unpacked element. Expected Number but found Bool. 11 | 12 | stdin:2:14 13 | ╷ 14 | 2 │ let ys: List[Number] = [..bools]; 15 | ╵ ^~~~~~ 16 | Note: Expected Number because of this annotation. 17 | 18 | stdin:1:14 19 | ╷ 20 | 1 │ let bools = [true, false]; 21 | ╵ ^~~~ 22 | Note: Found Bool because of this value. 23 | -------------------------------------------------------------------------------- /golden/error/type_index_dict_fn_key.test: -------------------------------------------------------------------------------- 1 | let f = x => 0; 2 | let xs = { f: 0 }; 3 | xs[{} | std] 4 | 5 | # output: 6 | stdin:3:4 7 | ╷ 8 | 3 │ xs[{} | std] 9 | ╵ ^~~~~~~~ 10 | Error: Type mismatch. Expected a value that fits this type: 11 | 12 | (x: Any) -> Number 13 | 14 | But got this value: 15 | 16 | { 17 | empty_set = std.empty_set, 18 | format_json = std.format_json, 19 | range = std.range, 20 | read_file_utf8 = std.read_file_utf8, 21 | } 22 | 23 | stdin:1:9 24 | ╷ 25 | 1 │ let f = x => 0; 26 | ╵ ^~~~~~ 27 | Note: Expected Function because of this value. 28 | -------------------------------------------------------------------------------- /golden/error_json/std_format_json_builtin_method.test: -------------------------------------------------------------------------------- 1 | // Similar to builtin_method.test, except here the error is generated inside 2 | // the call to format_json. We nest the invalid value in a list to also stress 3 | // the location reporting. 4 | std.format_json(["test".len]) 5 | 6 | # output: 7 | stdin:4:17 8 | ╷ 9 | 4 │ std.format_json(["test".len]) 10 | ╵ ^~~~~~~~~~~~ 11 | in value 12 | at index 0 13 | Error: Methods cannot be exported as json. 14 | 15 | stdin:4:16 16 | ╷ 17 | 4 │ std.format_json(["test".len]) 18 | ╵ ^ 19 | In call to function 'std.format_json'. 20 | -------------------------------------------------------------------------------- /golden/fmt/string_triple_quotes.test: -------------------------------------------------------------------------------- 1 | [ 2 | // An empty """-string remains empty. 3 | """""", 4 | // This is a regression test, should not escape spaces before the "" even 5 | // though the lexer breaks the token up at that point. 6 | """ 7 | "" <- That breaks up this line for the lexer.""", 8 | ] 9 | 10 | # output: 11 | [ 12 | // An empty """-string remains empty. 13 | """""", 14 | // This is a regression test, should not escape spaces before the "" even 15 | // though the lexer breaks the token up at that point. 16 | """ 17 | "" <- That breaks up this line for the lexer.""", 18 | ] 19 | -------------------------------------------------------------------------------- /golden/types/static_union_supertype_err.test: -------------------------------------------------------------------------------- 1 | let a0: Union[Null, Number] = null; 2 | let b0: String = a0; 3 | b0 4 | 5 | # output: 6 | stdin:2:18 7 | ╷ 8 | 2 │ let b0: String = a0; 9 | ╵ ^~ 10 | Error: Type mismatch. Expected String but found this type: 11 | 12 | Union[Null, Number] 13 | 14 | stdin:2:9 15 | ╷ 16 | 2 │ let b0: String = a0; 17 | ╵ ^~~~~~ 18 | Note: Expected String because of this annotation. 19 | 20 | stdin:1:9 21 | ╷ 22 | 1 │ let a0: Union[Null, Number] = null; 23 | ╵ ^~~~~~~~~~~~~~~~~~~ 24 | Note: Found Union because of this annotation. 25 | -------------------------------------------------------------------------------- /pyrcl/Cargo.toml: -------------------------------------------------------------------------------- 1 | # This file is generated, see build.rcl in the repository root. 2 | 3 | [dependencies] 4 | pyo3 = { default-features = false, features = [ 5 | "macros", 6 | "extension-module", 7 | "abi3", 8 | "abi3-py310", 9 | ], version = "0.20.2" } 10 | rcl = { path = ".." } 11 | 12 | [lib] 13 | crate-type = ["cdylib"] 14 | doc = false 15 | name = "rcl" 16 | 17 | [package] 18 | authors = ["Ruud van Asseldonk "] 19 | description = "Python bindings to the RCL configuration language." 20 | edition = "2021" 21 | license = "Apache-2.0" 22 | name = "pyrcl" 23 | version = "0.12.0" 24 | -------------------------------------------------------------------------------- /Cargo.toml: -------------------------------------------------------------------------------- 1 | # This file is generated, see build.rcl in the repository root. 2 | 3 | [dependencies] 4 | unicode-width = "0.1.10" 5 | 6 | [package] 7 | authors = ["Ruud van Asseldonk "] 8 | description = "A reasonable configuration language." 9 | edition = "2021" 10 | license = "Apache-2.0" 11 | name = "rcl" 12 | version = "0.12.0" 13 | 14 | [profile] 15 | release = { lto = "thin", panic = "abort", strip = true } 16 | release-wasm = { codegen-units = 1, inherits = "release", lto = "fat", opt-level = "z" } 17 | 18 | [workspace] 19 | members = ["fuzz", "pyrcl", "grammar/tree-sitter-rcl", "wasm"] 20 | -------------------------------------------------------------------------------- /golden/error/type_let_function_arity.test: -------------------------------------------------------------------------------- 1 | let f: (Number, String) -> Bool = (x, y, z) => false; 2 | null 3 | 4 | # output: 5 | stdin:1:35 6 | ╷ 7 | 1 │ let f: (Number, String) -> Bool = (x, y, z) => false; 8 | ╵ ^~~~~~~~~~~~~~~~~~ 9 | Error: Type mismatch. Expected this type: 10 | 11 | (Number, String) -> Bool 12 | 13 | But found this type: 14 | 15 | (x: Any, y: Any, z: Any) -> Bool 16 | 17 | stdin:1:8 18 | ╷ 19 | 1 │ let f: (Number, String) -> Bool = (x, y, z) => false; 20 | ╵ ^~~~~~~~~~~~~~~~~~~~~~~~ 21 | Note: Expected Function because of this annotation. 22 | -------------------------------------------------------------------------------- /golden/types/static_collection_set_inner.test: -------------------------------------------------------------------------------- 1 | let xs: Set[Number] = {0}; 2 | let ys: Set[Bool] = xs; 3 | null 4 | 5 | # output: 6 | stdin:2:21 7 | ╷ 8 | 2 │ let ys: Set[Bool] = xs; 9 | ╵ ^~ 10 | Error: Type mismatch inside this type: 11 | 12 | Set[] 13 | 14 | At E1: Expected Bool but found Number. 15 | 16 | stdin:2:13 17 | ╷ 18 | 2 │ let ys: Set[Bool] = xs; 19 | ╵ ^~~~ 20 | Note: Expected Bool because of this annotation. 21 | 22 | stdin:1:13 23 | ╷ 24 | 1 │ let xs: Set[Number] = {0}; 25 | ╵ ^~~~~~ 26 | Note: Found Number because of this annotation. 27 | -------------------------------------------------------------------------------- /golden/error/type_msg_function.test: -------------------------------------------------------------------------------- 1 | // This test formatting of a function type in the error message. 2 | let f: (Number, String) -> Bool = "not a function"; 3 | null 4 | 5 | # output: 6 | stdin:2:35 7 | ╷ 8 | 2 │ let f: (Number, String) -> Bool = "not a function"; 9 | ╵ ^~~~~~~~~~~~~~~~ 10 | Error: Type mismatch. Expected this type: 11 | 12 | (Number, String) -> Bool 13 | 14 | But found String. 15 | 16 | stdin:2:8 17 | ╷ 18 | 2 │ let f: (Number, String) -> Bool = "not a function"; 19 | ╵ ^~~~~~~~~~~~~~~~~~~~~~~~ 20 | Note: Expected Function because of this annotation. 21 | -------------------------------------------------------------------------------- /golden/error/std_list_map_error.test: -------------------------------------------------------------------------------- 1 | [1, 2, 3].map(x => assert false: "This crashes"; x) 2 | 3 | # output: 4 | stdin:1:27 5 | ╷ 6 | 1 │ [1, 2, 3].map(x => assert false: "This crashes"; x) 7 | ╵ ^~~~~ 8 | Error: Assertion failed. This crashes 9 | 10 | stdin:1:15 11 | ╷ 12 | 1 │ [1, 2, 3].map(x => assert false: "This crashes"; x) 13 | ╵ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 14 | In internal call to mapping function from 'List.map'. 15 | 16 | stdin:1:14 17 | ╷ 18 | 1 │ [1, 2, 3].map(x => assert false: "This crashes"; x) 19 | ╵ ^ 20 | In call to method 'List.map'. 21 | -------------------------------------------------------------------------------- /golden/types/static_collection_list_inner.test: -------------------------------------------------------------------------------- 1 | let xs: List[Number] = [0]; 2 | let ys: List[Bool] = xs; 3 | null 4 | 5 | # output: 6 | stdin:2:22 7 | ╷ 8 | 2 │ let ys: List[Bool] = xs; 9 | ╵ ^~ 10 | Error: Type mismatch inside this type: 11 | 12 | List[] 13 | 14 | At E1: Expected Bool but found Number. 15 | 16 | stdin:2:14 17 | ╷ 18 | 2 │ let ys: List[Bool] = xs; 19 | ╵ ^~~~ 20 | Note: Expected Bool because of this annotation. 21 | 22 | stdin:1:14 23 | ╷ 24 | 1 │ let xs: List[Number] = [0]; 25 | ╵ ^~~~~~ 26 | Note: Found Number because of this annotation. 27 | -------------------------------------------------------------------------------- /golden/error/parse_binop_precedence.test: -------------------------------------------------------------------------------- 1 | let flags = ["no", "se", "dk"]; 2 | flags.contains("se") or flags.contains("dk") and flags.contains("no") 3 | 4 | # output: 5 | stdin:2:46 6 | ╷ 7 | 2 │ flags.contains("se") or flags.contains("dk") and flags.contains("no") 8 | ╵ ^~~ 9 | Error: Parentheses are needed to clarify the precedence of this operator. 10 | 11 | stdin:2:22 12 | ╷ 13 | 2 │ flags.contains("se") or flags.contains("dk") and flags.contains("no") 14 | ╵ ^~ 15 | Note: Without parentheses, it is not clear whether this operator should take precedence. 16 | -------------------------------------------------------------------------------- /golden/error/runtime_unknown_field_list.test: -------------------------------------------------------------------------------- 1 | let turbo_encabulator = [ 2 | "differential girdlespacing", 3 | "hydrocoptic marzlevanes", 4 | "pentametric fan", 5 | "spurving bearings", 6 | ]; 7 | turbo_encabulator.is_prefabulated 8 | 9 | # output: 10 | stdin:7:19 11 | ╷ 12 | 7 │ turbo_encabulator.is_prefabulated 13 | ╵ ^~~~~~~~~~~~~~~ 14 | Error: Unknown field. 15 | 16 | stdin:7:1 17 | ╷ 18 | 7 │ turbo_encabulator.is_prefabulated 19 | ╵ ^~~~~~~~~~~~~~~~~ 20 | Note: On value: [ 21 | "differential girdlespacing", 22 | "hydrocoptic marzlevanes", 23 | "pentametric fan", 24 | "spurving bearings", 25 | ] 26 | -------------------------------------------------------------------------------- /golden/error/runtime_import_cycle.test: -------------------------------------------------------------------------------- 1 | import "_import_cycle_a.rcl" 2 | 3 | # output: 4 | _import_cycle_c.rcl:1:8 5 | ╷ 6 | 1 │ import "_import_cycle_a.rcl" 7 | ╵ ^~~~~~~~~~~~~~~~~~~~~ 8 | Error: This import creates a cycle. 9 | 10 | _import_cycle_b.rcl:1:8 11 | ╷ 12 | 1 │ import "_import_cycle_c.rcl" 13 | ╵ ^~~~~~~~~~~~~~~~~~~~~ 14 | Note: Imported here. 15 | 16 | _import_cycle_a.rcl:1:8 17 | ╷ 18 | 1 │ import "_import_cycle_b.rcl" 19 | ╵ ^~~~~~~~~~~~~~~~~~~~~ 20 | Note: Imported here. 21 | 22 | stdin:1:8 23 | ╷ 24 | 1 │ import "_import_cycle_a.rcl" 25 | ╵ ^~~~~~~~~~~~~~~~~~~~~ 26 | Note: Imported here. 27 | -------------------------------------------------------------------------------- /golden/error/std_list_any_inner_error.test: -------------------------------------------------------------------------------- 1 | [1, 2, 3].any(x => assert x != 2: "Inner error!"; false) 2 | 3 | # output: 4 | stdin:1:27 5 | ╷ 6 | 1 │ [1, 2, 3].any(x => assert x != 2: "Inner error!"; false) 7 | ╵ ^~~~~~ 8 | Error: Assertion failed. Inner error! 9 | 10 | stdin:1:15 11 | ╷ 12 | 1 │ [1, 2, 3].any(x => assert x != 2: "Inner error!"; false) 13 | ╵ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 14 | In internal call to predicate from 'List.any'. 15 | 16 | stdin:1:14 17 | ╷ 18 | 1 │ [1, 2, 3].any(x => assert x != 2: "Inner error!"; false) 19 | ╵ ^ 20 | In call to method 'List.any'. 21 | -------------------------------------------------------------------------------- /golden/toml/table_in_table.test: -------------------------------------------------------------------------------- 1 | { 2 | obj = { 3 | // Formatting this on one line exceeds the width of the pretty-printer, it 4 | // wants to break it. But it can't because TOML does not allow that. This is 5 | // a regression test, in the past we did add line breaks and the output 6 | // could not be parsed by Python tomllib or Rust toml crate. 7 | very_long_name_that_would_cause = { 8 | The_output_to_be = "line-wrapped", 9 | over_multiple_lines = true, 10 | }, 11 | }, 12 | } 13 | 14 | # output: 15 | [obj] 16 | very_long_name_that_would_cause = { The_output_to_be = "line-wrapped", over_multiple_lines = true } 17 | -------------------------------------------------------------------------------- /grammar/tree-sitter-rcl/queries/highlights_nvim.scm: -------------------------------------------------------------------------------- 1 | ["for" "in"] @keyword.repeat 2 | ["assert" "trace"] @keyword.debug 3 | ["if" "else"] @keyword.conditional 4 | ["import"] @keyword.import 5 | ["let"] @keyword.storage 6 | (unop_keyword) @keyword.operator 7 | (binop_keyword) @keyword.operator 8 | 9 | (comment) @comment 10 | (shebang) @keyword.directive 11 | (number) @number 12 | (bool) @boolean 13 | (null) @constant.builtin 14 | 15 | (string) @string 16 | (string_escape) @string.escape 17 | (string_hole (["{" "}"] @string.escape)) 18 | 19 | (seq_assoc_expr field: (string) @property) 20 | (seq_assoc_ident field: (ident) @property) 21 | 22 | (type_term) @type 23 | -------------------------------------------------------------------------------- /golden/error/std_list_fold_depth_set.test: -------------------------------------------------------------------------------- 1 | // This is a regression test for a hang due to deeply nested values. 2 | // The hang is even worse for e.g. x => {x, {x}}, but that would make 3 | // the test too slow. 4 | std.range(0, 1000).fold([], (x, _) => {x, 1}) 5 | 6 | # output: 7 | stdin:4:20 8 | ╷ 9 | 4 │ std.range(0, 1000).fold([], (x, _) => {x, 1}) 10 | ╵ ^~~~ 11 | Error: Accumulator exceeds the maximum nesting depth: { 12 | 1, 13 | {1, {1, {1, {1, {1, {1, {1, {1, []}}}}}}}}, 14 | } 15 | 16 | stdin:4:24 17 | ╷ 18 | 4 │ std.range(0, 1000).fold([], (x, _) => {x, 1}) 19 | ╵ ^ 20 | In call to method 'List.fold'. 21 | -------------------------------------------------------------------------------- /golden/error/type_let_mismatch_long_type.test: -------------------------------------------------------------------------------- 1 | // This test tests pretty-printing of types. 2 | let x: Dict[ 3 | String, 4 | Dict[String, Dict[String, Dict[String, List[List[List[Dict[Number, String]]]]]]], 5 | ] = null; 6 | x 7 | 8 | # output: 9 | stdin:5:5 10 | ╷ 11 | 5 │ ] = null; 12 | ╵ ^~~~ 13 | Error: Type mismatch. Expected this type: 14 | 15 | Dict[ 16 | String, 17 | Dict[ 18 | String, 19 | Dict[String, Dict[String, List[List[List[Dict[Number, String]]]]]], 20 | ], 21 | ] 22 | 23 | But found Null. 24 | 25 | stdin:2:8 26 | ╷ 27 | 2 │ let x: Dict[ 28 | ╵ ^~~~~ 29 | Note: Expected Dict because of this annotation. 30 | -------------------------------------------------------------------------------- /golden/types/runtime_dict_key.test: -------------------------------------------------------------------------------- 1 | let xs = { not_int = 42 }; 2 | let ys: Dict[Any, Number] = xs; 3 | // Runtime type error: string "not_int" is not of type Number. 4 | let zs: Dict[Number, Number] = ys; 5 | null 6 | 7 | # output: 8 | stdin:4:32 9 | ╷ 10 | 4 │ let zs: Dict[Number, Number] = ys; 11 | ╵ ^~ 12 | in value 13 | at key "not_int" 14 | Error: Type mismatch. Expected a value that fits this type: 15 | 16 | Number 17 | 18 | But got this value: 19 | 20 | "not_int" 21 | 22 | stdin:4:14 23 | ╷ 24 | 4 │ let zs: Dict[Number, Number] = ys; 25 | ╵ ^~~~~~ 26 | Note: Expected Number because of this annotation. 27 | -------------------------------------------------------------------------------- /golden/types/runtime_dict_value.test: -------------------------------------------------------------------------------- 1 | let xs = { answer = "42" }; 2 | let ys: Dict[String, Any] = xs; 3 | // Runtime type error: string "42" is not of type Number. 4 | let zs: Dict[String, Number] = ys; 5 | null 6 | 7 | # output: 8 | stdin:4:32 9 | ╷ 10 | 4 │ let zs: Dict[String, Number] = ys; 11 | ╵ ^~ 12 | in value 13 | at key "answer" 14 | Error: Type mismatch. Expected a value that fits this type: 15 | 16 | Number 17 | 18 | But got this value: 19 | 20 | "42" 21 | 22 | stdin:4:22 23 | ╷ 24 | 4 │ let zs: Dict[String, Number] = ys; 25 | ╵ ^~~~~~ 26 | Note: Expected Number because of this annotation. 27 | -------------------------------------------------------------------------------- /golden/error/call_stack_context_method.test: -------------------------------------------------------------------------------- 1 | // The method String.len is not a valid key selector function, but it does have 2 | // a name, so it gets mentioned in the error context. 3 | let f = "ABC".len; 4 | [1, 2, 3].group_by(f) 5 | 6 | # output: 7 | stdin:4:20 8 | ╷ 9 | 4 │ [1, 2, 3].group_by(f) 10 | ╵ ^ 11 | Error: Unexpected argument. 'String.len' takes 0 arguments, but got 1. 12 | 13 | stdin:4:20 14 | ╷ 15 | 4 │ [1, 2, 3].group_by(f) 16 | ╵ ^ 17 | In internal call to key selector from 'List.group_by'. 18 | 19 | stdin:4:19 20 | ╷ 21 | 4 │ [1, 2, 3].group_by(f) 22 | ╵ ^ 23 | In call to method 'List.group_by'. 24 | -------------------------------------------------------------------------------- /golden/json/function_compare.test: -------------------------------------------------------------------------------- 1 | let f1 = _ => 42; 2 | let f2 = _ => 42; 3 | 4 | // Functions f1 and f2 are syntactically identical, but defined in different 5 | // places, so they are considered distinct, and f1 orders before f2 because it 6 | // was defined earlier in the file. 7 | let r1 = [f1 == f1, f1 == f2]; 8 | 9 | // Even when functions are defined in the same place, their captures can be 10 | // different. 11 | let fs = [for x in [2, 1]: _ => x]; 12 | let r2 = [fs[0] == fs[0], fs[0] == fs[1]]; 13 | 14 | // Functions can be put in a set. 15 | let all_fs = {f1, f2, for f in fs: f}; 16 | 17 | [r1, r2, all_fs.len()] 18 | 19 | # output: 20 | [[true, false], [true, false], 4] 21 | -------------------------------------------------------------------------------- /golden/types/static_collection_dict_key.test: -------------------------------------------------------------------------------- 1 | let xs: Dict[Number, Number] = {0: 0}; 2 | let ys: Dict[Bool, Number] = xs; 3 | null 4 | 5 | # output: 6 | stdin:2:30 7 | ╷ 8 | 2 │ let ys: Dict[Bool, Number] = xs; 9 | ╵ ^~ 10 | Error: Type mismatch inside this type: 11 | 12 | Dict[, Number] 13 | 14 | At E1: Expected Bool but found Number. 15 | 16 | stdin:2:14 17 | ╷ 18 | 2 │ let ys: Dict[Bool, Number] = xs; 19 | ╵ ^~~~ 20 | Note: Expected Bool because of this annotation. 21 | 22 | stdin:1:14 23 | ╷ 24 | 1 │ let xs: Dict[Number, Number] = {0: 0}; 25 | ╵ ^~~~~~ 26 | Note: Found Number because of this annotation. 27 | -------------------------------------------------------------------------------- /golden/error/call_stack_context_function.test: -------------------------------------------------------------------------------- 1 | // The function std.range is not a valid key selector function, but it does have 2 | // a name, so it gets mentioned in the error context. 3 | let f = std.range; 4 | [1, 2, 3].group_by(f) 5 | 6 | # output: 7 | stdin:4:20 8 | ╷ 9 | 4 │ [1, 2, 3].group_by(f) 10 | ╵ ^ 11 | Error: Missing argument 'upper'. 'std.range' takes 2 arguments, but got 1. 12 | 13 | stdin:4:20 14 | ╷ 15 | 4 │ [1, 2, 3].group_by(f) 16 | ╵ ^ 17 | In internal call to key selector from 'List.group_by'. 18 | 19 | stdin:4:19 20 | ╷ 21 | 4 │ [1, 2, 3].group_by(f) 22 | ╵ ^ 23 | In call to method 'List.group_by'. 24 | -------------------------------------------------------------------------------- /golden/error/std_set_transitive_closure_err.test: -------------------------------------------------------------------------------- 1 | {0}.transitive_closure(x => assert false: "Oops."; x) 2 | 3 | # output: 4 | stdin:1:36 5 | ╷ 6 | 1 │ {0}.transitive_closure(x => assert false: "Oops."; x) 7 | ╵ ^~~~~ 8 | Error: Assertion failed. Oops. 9 | 10 | stdin:1:24 11 | ╷ 12 | 1 │ {0}.transitive_closure(x => assert false: "Oops."; x) 13 | ╵ ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 14 | In internal call to the expansion function from 'transitive_closure'. 15 | 16 | stdin:1:23 17 | ╷ 18 | 1 │ {0}.transitive_closure(x => assert false: "Oops."; x) 19 | ╵ ^ 20 | In call to method 'Set.transitive_closure'. 21 | -------------------------------------------------------------------------------- /golden/fmt/shebang.test: -------------------------------------------------------------------------------- 1 | #! A shebang line is allowed at the start of the document. 2 | 3 | // It is also allowed in the middle, just because that was easier to implement. 4 | // It ought to not be allowed, but it is. The shebang has no meaning, but is 5 | // preserved for formatting purposes. 6 | 7 | #! Another shebang line. 8 | 9 | true 10 | 11 | # output: 12 | #! A shebang line is allowed at the start of the document. 13 | 14 | // It is also allowed in the middle, just because that was easier to implement. 15 | // It ought to not be allowed, but it is. The shebang has no meaning, but is 16 | // preserved for formatting purposes. 17 | 18 | #! Another shebang line. 19 | 20 | true 21 | -------------------------------------------------------------------------------- /grammar/tree-sitter-rcl/Cargo.toml: -------------------------------------------------------------------------------- 1 | # This file is generated, see build.rcl in the repository root. 2 | 3 | [build-dependencies] 4 | cc = "1.0" 5 | 6 | [dependencies] 7 | tree-sitter = "0.20.10" 8 | 9 | [lib] 10 | path = "bindings/rust/lib.rs" 11 | 12 | [package] 13 | authors = ["Ruud van Asseldonk "] 14 | build = "bindings/rust/build.rs" 15 | categories = ["parsing", "text-editors"] 16 | description = "RCL grammar for the tree-sitter parsing library." 17 | edition = "2021" 18 | include = ["bindings/rust/*", "grammar.js", "queries/*", "src/*"] 19 | keywords = ["incremental", "parsing", "rcl"] 20 | license = "Apache-2.0" 21 | name = "tree-sitter-rcl" 22 | version = "0.12.0" 23 | -------------------------------------------------------------------------------- /golden/types/static_collection_dict_value.test: -------------------------------------------------------------------------------- 1 | let xs: Dict[Number, Number] = {0: 0}; 2 | let ys: Dict[Number, Bool] = xs; 3 | null 4 | 5 | # output: 6 | stdin:2:30 7 | ╷ 8 | 2 │ let ys: Dict[Number, Bool] = xs; 9 | ╵ ^~ 10 | Error: Type mismatch inside this type: 11 | 12 | Dict[Number, ] 13 | 14 | At E1: Expected Bool but found Number. 15 | 16 | stdin:2:22 17 | ╷ 18 | 2 │ let ys: Dict[Number, Bool] = xs; 19 | ╵ ^~~~ 20 | Note: Expected Bool because of this annotation. 21 | 22 | stdin:1:22 23 | ╷ 24 | 1 │ let xs: Dict[Number, Number] = {0: 0}; 25 | ╵ ^~~~~~ 26 | Note: Found Number because of this annotation. 27 | -------------------------------------------------------------------------------- /grammar/zed/extension.rcl: -------------------------------------------------------------------------------- 1 | let root = import "//Cargo.rcl"; 2 | 3 | { 4 | id = "rcl", 5 | name = "RCL", 6 | description = "Support for the RCL configuration language.", 7 | version = root.package.version, 8 | schema_version = 1, 9 | authors = root.package.authors, 10 | repository = "https://github.com/rcl-lang/zed-rcl", 11 | 12 | grammars = { 13 | rcl = { 14 | repository = "https://github.com/rcl-lang/tree-sitter-rcl", 15 | // When tools/update_repos.py exports this to the zed-rcl repository, it 16 | // replaces this commit with the HEAD of the tree-sitter-rcl repository. 17 | commit = "9e14baa313f79d1717cf81e8e35f051c7da77073", 18 | }, 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /golden/error/parse_unop_in_binop.test: -------------------------------------------------------------------------------- 1 | [ 2 | // In final position, an unop in binop is allowed: 3 | true and not false, 4 | true and true and not false, 5 | 10 > -1, 6 | 10 * -1, 7 | 8 | // In the middle it would be confusing, so it's disallowed: 9 | true and not false and true 10 | ] 11 | 12 | # output: 13 | stdin:9:22 14 | ╷ 15 | 9 │ true and not false and true 16 | ╵ ^~~ 17 | Error: Parentheses are needed to clarify the precedence of this operator. 18 | 19 | stdin:9:12 20 | ╷ 21 | 9 │ true and not false and true 22 | ╵ ^~~ 23 | Note: Without parentheses, it is not clear whether this operator applies only to the left-hand side, or the full expression. 24 | -------------------------------------------------------------------------------- /golden/json/function_capture_environment.test: -------------------------------------------------------------------------------- 1 | // Case 1: Even though we shadow `x` later, the capture is for the earlier 2 | // definition. 3 | let x = 42; 4 | let f = y => x + y; 5 | let x = 0; 6 | // Should return 43, not 1. 7 | let r1 = f(1); 8 | 9 | // Case 2: The function arguments shadow any previously defined variables. 10 | let x = 42; 11 | let f = x => x + 1; 12 | // Should return 1, not 43. 13 | let r2 = f(0); 14 | 15 | // Case 3: The captures can be different even for a function at the same source 16 | // location. 17 | let fs = [for k in [1, 2, 3]: x => x + k]; 18 | let r3 = [for f in fs: f(10)]; 19 | 20 | { r1 = r1, r2 = r2, r3 = r3 } 21 | 22 | # output: 23 | {"r1": 43, "r2": 1, "r3": [11, 12, 13]} 24 | -------------------------------------------------------------------------------- /golden/error/type_in_unpack_key.test: -------------------------------------------------------------------------------- 1 | let bool_keys = { false: 0, true: 1 }; 2 | let ys: Dict[Number, Any] = { ...bool_keys }; 3 | ys 4 | 5 | # output: 6 | stdin:2:34 7 | ╷ 8 | 2 │ let ys: Dict[Number, Any] = { ...bool_keys }; 9 | ╵ ^~~~~~~~~ 10 | Error: Type mismatch inside this type: 11 | 12 | Dict[, Number] 13 | 14 | At E1: Expected Number but found Bool. 15 | 16 | stdin:2:14 17 | ╷ 18 | 2 │ let ys: Dict[Number, Any] = { ...bool_keys }; 19 | ╵ ^~~~~~ 20 | Note: Expected Number because of this annotation. 21 | 22 | stdin:1:19 23 | ╷ 24 | 1 │ let bool_keys = { false: 0, true: 1 }; 25 | ╵ ^~~~~ 26 | Note: Found Bool because of this value. 27 | --------------------------------------------------------------------------------