diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..1a71cb6 --- /dev/null +++ b/LICENSE @@ -0,0 +1,131 @@ +# PolyForm Noncommercial License 1.0.0 + + + +## Acceptance + +In order to get any license under these terms, you must agree +to them as both strict obligations and conditions to all +your licenses. + +## Copyright License + +The licensor grants you a copyright license for the +software to do everything you might do with the software +that would otherwise infringe the licensor's copyright +in it for any permitted purpose. However, you may +only distribute the software according to [Distribution +License](#distribution-license) and make changes or new works +based on the software according to [Changes and New Works +License](#changes-and-new-works-license). + +## Distribution License + +The licensor grants you an additional copyright license +to distribute copies of the software. Your license +to distribute covers distributing the software with +changes and new works permitted by [Changes and New Works +License](#changes-and-new-works-license). + +## Notices + +You must ensure that anyone who gets a copy of any part of +the software from you also gets a copy of these terms or the +URL for them above, as well as copies of any plain-text lines +beginning with `Required Notice:` that the licensor provided +with the software. For example: + +> Required Notice: Copyright Yoyodyne, Inc. (http://example.com) + +## Changes and New Works License + +The licensor grants you an additional copyright license to +make changes and new works based on the software for any +permitted purpose. + +## Patent License + +The licensor grants you a patent license for the software that +covers patent claims the licensor can license, or becomes able +to license, that you would infringe by using the software. + +## Noncommercial Purposes + +Any noncommercial purpose is a permitted purpose. + +## Personal Uses + +Personal use for research, experiment, and testing for +the benefit of public knowledge, personal study, private +entertainment, hobby projects, amateur pursuits, or religious +observance, without any anticipated commercial application, +is use for a permitted purpose. + +## Noncommercial Organizations + +Use by any charitable organization, educational institution, +public research organization, public safety or health +organization, environmental protection organization, +or government institution is use for a permitted purpose +regardless of the source of funding or obligations resulting +from the funding. + +## Fair Use + +You may have "fair use" rights for the software under the +law. These terms do not limit them. + +## No Other Rights + +These terms do not allow you to sublicense or transfer any of +your licenses to anyone else, or prevent the licensor from +granting licenses to anyone else. These terms do not imply +any other licenses. + +## Patent Defense + +If you make any written claim that the software infringes or +contributes to infringement of any patent, your patent license +for the software granted under these terms ends immediately. If +your company makes such a claim, your patent license ends +immediately for work on behalf of your company. + +## Violations + +The first time you are notified in writing that you have +violated any of these terms, or done anything with the software +not covered by your licenses, your licenses can nonetheless +continue if you come into full compliance with these terms, +and take practical steps to correct past violations, within +32 days of receiving notice. Otherwise, all your licenses +end immediately. + +## No Liability + +***As far as the law allows, the software comes as is, without +any warranty or condition, and the licensor will not be liable +to you for any damages arising out of these terms or the use +or nature of the software, under any kind of legal claim.*** + +## Definitions + +The **licensor** is the individual or entity offering these +terms, and the **software** is the software the licensor makes +available under these terms. + +**You** refers to the individual or entity agreeing to these +terms. + +**Your company** is any legal entity, sole proprietorship, +or other kind of organization that you work for, plus all +organizations that have control over, are under the control of, +or are under common control with that organization. **Control** +means ownership of substantially all the assets of an entity, +or the power to direct its management and policies by vote, +contract, or otherwise. Control can be direct or indirect. + +**Your licenses** are all the licenses granted to you for the +software under these terms. + +**Use** means anything you do with the software requiring one +of your licenses. diff --git a/Makefile b/Makefile index 8891eb1..613296c 100644 --- a/Makefile +++ b/Makefile @@ -89,7 +89,10 @@ TEST_ARRAY_INIT_OBJECTS = $(TEST_ARRAY_INIT_SOURCES:.c=.o) TEST_INSTANCEOF_SOURCES = tests/test_instanceof.c TEST_INSTANCEOF_OBJECTS = $(TEST_INSTANCEOF_SOURCES:.c=.o) -all: test_lexer test_parser test_semantic test_ir test_runtime test_strings test_arrays test_objects test_instance_methods test_fileio test_dowhile test_switch test_math test_string_methods test_static test_interfaces test_exceptions test_ternary test_bitwise test_enhanced_for test_array_init test_instanceof +TEST_SHORTCIRCUIT_SOURCES = tests/test_shortcircuit.c +TEST_SHORTCIRCUIT_OBJECTS = $(TEST_SHORTCIRCUIT_SOURCES:.c=.o) + +all: test_lexer test_parser test_semantic test_ir test_runtime test_strings test_arrays test_objects test_instance_methods test_fileio test_dowhile test_switch test_math test_string_methods test_static test_interfaces test_exceptions test_ternary test_bitwise test_enhanced_for test_array_init test_instanceof test_shortcircuit test_lexer: $(LEXER_OBJECTS) $(TEST_LEXER_OBJECTS) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) @@ -157,6 +160,9 @@ test_array_init: $(LEXER_OBJECTS) $(PARSER_OBJECTS) $(TYPES_OBJECTS) $(SEMANTIC_ test_instanceof: $(LEXER_OBJECTS) $(PARSER_OBJECTS) $(TYPES_OBJECTS) $(SEMANTIC_OBJECTS) $(IR_OBJECTS) $(RUNTIME_OBJECTS) $(TEST_INSTANCEOF_OBJECTS) $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) +test_shortcircuit: $(LEXER_OBJECTS) $(PARSER_OBJECTS) $(TYPES_OBJECTS) $(SEMANTIC_OBJECTS) $(IR_OBJECTS) $(RUNTIME_OBJECTS) $(TEST_SHORTCIRCUIT_OBJECTS) + $(CC) $(CFLAGS) -o $@ $^ $(LDFLAGS) + %.o: %.c $(CC) $(CFLAGS) -c $< -o $@ @@ -222,8 +228,8 @@ clean: $(PHASE0_OBJECTS) \ $(TEST_LEXER_OBJECTS) $(TEST_PARSER_OBJECTS) $(TEST_SEMANTIC_OBJECTS) $(TEST_IR_OBJECTS) $(TEST_RUNTIME_OBJECTS) \ $(TEST_STRINGS_OBJECTS) $(TEST_ARRAYS_OBJECTS) $(TEST_OBJECTS_OBJECTS) $(TEST_INSTANCE_OBJECTS) $(TEST_FILEIO_OBJECTS) \ - $(TEST_DOWHILE_OBJECTS) $(TEST_SWITCH_OBJECTS) $(TEST_MATH_OBJECTS) $(TEST_STRING_METHODS_OBJECTS) $(TEST_STATIC_OBJECTS) $(TEST_INTERFACES_OBJECTS) $(TEST_EXCEPTIONS_OBJECTS) $(TEST_TERNARY_OBJECTS) $(TEST_BITWISE_OBJECTS) $(TEST_ENHANCED_FOR_OBJECTS) $(TEST_ARRAY_INIT_OBJECTS) $(TEST_INSTANCEOF_OBJECTS) $(TEST_BENCHMARK_OBJECTS) \ - test_lexer test_parser test_semantic test_ir test_runtime test_strings test_arrays test_objects test_instance_methods test_fileio test_dowhile test_switch test_math test_string_methods test_static test_interfaces test_exceptions test_ternary test_bitwise test_enhanced_for test_array_init test_instanceof test_benchmark \ + $(TEST_DOWHILE_OBJECTS) $(TEST_SWITCH_OBJECTS) $(TEST_MATH_OBJECTS) $(TEST_STRING_METHODS_OBJECTS) $(TEST_STATIC_OBJECTS) $(TEST_INTERFACES_OBJECTS) $(TEST_EXCEPTIONS_OBJECTS) $(TEST_TERNARY_OBJECTS) $(TEST_BITWISE_OBJECTS) $(TEST_ENHANCED_FOR_OBJECTS) $(TEST_ARRAY_INIT_OBJECTS) $(TEST_INSTANCEOF_OBJECTS) $(TEST_SHORTCIRCUIT_OBJECTS) $(TEST_BENCHMARK_OBJECTS) \ + test_lexer test_parser test_semantic test_ir test_runtime test_strings test_arrays test_objects test_instance_methods test_fileio test_dowhile test_switch test_math test_string_methods test_static test_interfaces test_exceptions test_ternary test_bitwise test_enhanced_for test_array_init test_instanceof test_shortcircuit test_benchmark \ test_nanbox test_fastframe test_labeltable test_methodcache test_benchmark_pgo *.gcda */*.gcda .PHONY: all clean benchmark test_phase0 pgo test_benchmark_pgo_gen pgo_run test_benchmark_pgo test @@ -235,4 +241,5 @@ test: all ./test_fileio && ./test_dowhile && ./test_switch && ./test_math && ./test_string_methods && \ ./test_static && ./test_interfaces && ./test_exceptions && ./test_ternary && \ ./test_bitwise && ./test_enhanced_for && ./test_array_init && ./test_instanceof && \ + ./test_shortcircuit && \ echo "" && echo "=== All Tests Passed ===" diff --git a/ir/ir.h b/ir/ir.h index 409d958..8156ad8 100644 --- a/ir/ir.h +++ b/ir/ir.h @@ -34,6 +34,7 @@ typedef enum { RAVA_OP_OR, RAVA_OP_XOR, RAVA_OP_NOT, + RAVA_OP_BITNOT, RAVA_OP_SHL, RAVA_OP_SHR, RAVA_OP_USHR, diff --git a/ir/ir_gen.c b/ir/ir_gen.c index 9548a57..e53f809 100644 --- a/ir/ir_gen.c +++ b/ir/ir_gen.c @@ -141,11 +141,43 @@ static void _rava_ir_gen_expression(RavaIRGenerator_t *gen, RavaASTNode_t *expr) } case RAVA_AST_BINARY_EXPR: - _rava_ir_gen_binary_expr(gen, expr); + if (expr->data.binary.op == RAVA_BINOP_AND) { + _rava_ir_gen_expression(gen, expr->data.binary.left); + instr.opcode = RAVA_OP_DUP; + _rava_ir_emit(gen, instr); + int end_label = rava_instruction_list_new_label(gen->current_method->instructions); + instr.opcode = RAVA_OP_JUMP_IF_FALSE; + instr.operand.label_id = end_label; + _rava_ir_emit(gen, instr); + instr.opcode = RAVA_OP_POP; + _rava_ir_emit(gen, instr); + _rava_ir_gen_expression(gen, expr->data.binary.right); + instr.opcode = RAVA_OP_LABEL; + instr.operand.label_id = end_label; + _rava_ir_emit(gen, instr); + } else if (expr->data.binary.op == RAVA_BINOP_OR) { + _rava_ir_gen_expression(gen, expr->data.binary.left); + instr.opcode = RAVA_OP_DUP; + _rava_ir_emit(gen, instr); + int end_label = rava_instruction_list_new_label(gen->current_method->instructions); + instr.opcode = RAVA_OP_JUMP_IF_TRUE; + instr.operand.label_id = end_label; + _rava_ir_emit(gen, instr); + instr.opcode = RAVA_OP_POP; + _rava_ir_emit(gen, instr); + _rava_ir_gen_expression(gen, expr->data.binary.right); + instr.opcode = RAVA_OP_LABEL; + instr.operand.label_id = end_label; + _rava_ir_emit(gen, instr); + } else { + _rava_ir_gen_binary_expr(gen, expr); + } break; case RAVA_AST_UNARY_EXPR: - if (expr->data.unary.op == RAVA_UNOP_MINUS) { + if (expr->data.unary.op == RAVA_UNOP_PLUS) { + _rava_ir_gen_expression(gen, expr->data.unary.operand); + } else if (expr->data.unary.op == RAVA_UNOP_MINUS) { _rava_ir_gen_expression(gen, expr->data.unary.operand); instr.opcode = RAVA_OP_NEG; _rava_ir_emit(gen, instr); @@ -153,6 +185,10 @@ static void _rava_ir_gen_expression(RavaIRGenerator_t *gen, RavaASTNode_t *expr) _rava_ir_gen_expression(gen, expr->data.unary.operand); instr.opcode = RAVA_OP_NOT; _rava_ir_emit(gen, instr); + } else if (expr->data.unary.op == RAVA_UNOP_BITNOT) { + _rava_ir_gen_expression(gen, expr->data.unary.operand); + instr.opcode = RAVA_OP_BITNOT; + _rava_ir_emit(gen, instr); } else if (expr->data.unary.op == RAVA_UNOP_PREINC || expr->data.unary.op == RAVA_UNOP_PREDEC) { if (expr->data.unary.operand->type == RAVA_AST_IDENTIFIER_EXPR) { int local_index = _rava_ir_find_local(gen, expr->data.unary.operand->data.identifier.name); diff --git a/parser/parser_expressions.c b/parser/parser_expressions.c index ce52a90..c91b17a 100644 --- a/parser/parser_expressions.c +++ b/parser/parser_expressions.c @@ -534,6 +534,18 @@ static RavaASTNode_t* _rava_parser_parse_assignment(RavaParser_t *parser) { compound_op = RAVA_BINOP_DIV; } else if (_rava_parser_match(parser, RAVA_TOKEN_PERCENTASSIGN)) { compound_op = RAVA_BINOP_MOD; + } else if (_rava_parser_match(parser, RAVA_TOKEN_ANDASSIGN)) { + compound_op = RAVA_BINOP_BITAND; + } else if (_rava_parser_match(parser, RAVA_TOKEN_ORASSIGN)) { + compound_op = RAVA_BINOP_BITOR; + } else if (_rava_parser_match(parser, RAVA_TOKEN_CARETASSIGN)) { + compound_op = RAVA_BINOP_BITXOR; + } else if (_rava_parser_match(parser, RAVA_TOKEN_LSHIFTASSIGN)) { + compound_op = RAVA_BINOP_LSHIFT; + } else if (_rava_parser_match(parser, RAVA_TOKEN_RSHIFTASSIGN)) { + compound_op = RAVA_BINOP_RSHIFT; + } else if (_rava_parser_match(parser, RAVA_TOKEN_URSHIFTASSIGN)) { + compound_op = RAVA_BINOP_URSHIFT; } if (compound_op != (RavaBinaryOp_e)-1) { diff --git a/runtime/runtime.c b/runtime/runtime.c index b31d7c5..a81465c 100644 --- a/runtime/runtime.c +++ b/runtime/runtime.c @@ -1628,7 +1628,7 @@ static bool _rava_vm_execute_fast(RavaVM_t *vm, RavaCallFrame_t *frame) { &&op_load_local, &&op_load_field, &&op_load_static, &&op_load_array, &&op_store_local, &&op_store_field, &&op_store_static, &&op_store_array, &&op_add, &&op_sub, &&op_mul, &&op_div, &&op_mod, &&op_neg, - &&op_and, &&op_or, &&op_xor, &&op_not, &&op_shl, &&op_shr, &&op_ushr, + &&op_and, &&op_or, &&op_xor, &&op_not, &&op_bitnot, &&op_shl, &&op_shr, &&op_ushr, &&op_eq, &&op_ne, &&op_lt, &&op_le, &&op_gt, &&op_ge, &&op_jump, &&op_jump_if_true, &&op_jump_if_false, &&op_label, &&op_call, &&op_call_static, &&op_call_virtual, &&op_call_super, &&op_call_native, @@ -1925,6 +1925,12 @@ op_not: { DISPATCH(); } +op_bitnot: { + RavaValue_t a = STACK_POP(stack); + STACK_PUSH(stack, rava_value_int(~rava_value_as_int(a))); + DISPATCH(); +} + op_shl: { RavaValue_t b = STACK_POP(stack); RavaValue_t a = STACK_POP(stack); @@ -2855,7 +2861,7 @@ static bool _rava_vm_execute_ultrafast(RavaVM_t *vm, RavaMethod_t *entry_method) &&uf_load_local, &&uf_load_field, &&uf_load_static, &&uf_load_array, &&uf_store_local, &&uf_store_field, &&uf_store_static, &&uf_store_array, &&uf_add, &&uf_sub, &&uf_mul, &&uf_div, &&uf_mod, &&uf_neg, - &&uf_and, &&uf_or, &&uf_xor, &&uf_not, &&uf_shl, &&uf_shr, &&uf_ushr, + &&uf_and, &&uf_or, &&uf_xor, &&uf_not, &&uf_bitnot, &&uf_shl, &&uf_shr, &&uf_ushr, &&uf_eq, &&uf_ne, &&uf_lt, &&uf_le, &&uf_gt, &&uf_ge, &&uf_jump, &&uf_jump_if_true, &&uf_jump_if_false, &&uf_label, &&uf_call, &&uf_call_static, &&uf_call_virtual, &&uf_call_super, &&uf_call_native, @@ -2941,6 +2947,26 @@ uf_add: { RavaNanboxValue_t a = UF_POP(); if (rava_nanbox_is_int(a) && rava_nanbox_is_int(b)) { UF_PUSH(rava_nanbox_int(rava_nanbox_as_int(a) + rava_nanbox_as_int(b))); + } else if (rava_nanbox_is_string(a) || rava_nanbox_is_string(b)) { + char buf_a[64], buf_b[64]; + const char *str_a, *str_b; + if (rava_nanbox_is_string(a)) str_a = rava_nanbox_as_string(a) ? rava_nanbox_as_string(a) : "null"; + else if (rava_nanbox_is_int(a)) { snprintf(buf_a, 64, "%d", rava_nanbox_as_int(a)); str_a = buf_a; } + else if (rava_nanbox_is_long(a)) { snprintf(buf_a, 64, "%ld", (long)rava_nanbox_as_long(a)); str_a = buf_a; } + else if (rava_nanbox_is_double(a)) { snprintf(buf_a, 64, "%g", rava_nanbox_as_double(a)); str_a = buf_a; } + else if (rava_nanbox_is_bool(a)) { str_a = rava_nanbox_as_bool(a) ? "true" : "false"; } + else { str_a = "null"; } + if (rava_nanbox_is_string(b)) str_b = rava_nanbox_as_string(b) ? rava_nanbox_as_string(b) : "null"; + else if (rava_nanbox_is_int(b)) { snprintf(buf_b, 64, "%d", rava_nanbox_as_int(b)); str_b = buf_b; } + else if (rava_nanbox_is_long(b)) { snprintf(buf_b, 64, "%ld", (long)rava_nanbox_as_long(b)); str_b = buf_b; } + else if (rava_nanbox_is_double(b)) { snprintf(buf_b, 64, "%g", rava_nanbox_as_double(b)); str_b = buf_b; } + else if (rava_nanbox_is_bool(b)) { str_b = rava_nanbox_as_bool(b) ? "true" : "false"; } + else { str_b = "null"; } + size_t len = strlen(str_a) + strlen(str_b) + 1; + char *result = malloc(len); + strcpy(result, str_a); + strcat(result, str_b); + UF_PUSH(rava_nanbox_string(result)); } else if (rava_nanbox_is_long(a) || rava_nanbox_is_long(b)) { UF_PUSH(rava_nanbox_long(rava_nanbox_to_long(a) + rava_nanbox_to_long(b))); } else if (rava_nanbox_is_double(a) || rava_nanbox_is_double(b)) { @@ -3040,6 +3066,12 @@ uf_not: { UF_DISPATCH(); } +uf_bitnot: { + RavaNanboxValue_t a = UF_POP(); + UF_PUSH(rava_nanbox_long(~rava_nanbox_to_long(a))); + UF_DISPATCH(); +} + uf_shl: { RavaNanboxValue_t b = UF_POP(); RavaNanboxValue_t a = UF_POP();