Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
176 changes: 155 additions & 21 deletions src/zcl_mjs.clas.abap
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,14 @@ CLASS zcl_mjs IMPLEMENTATION.
CONTINUE.
ENDIF.
ENDIF.
IF lv_ch = `.` AND lv_i + 2 < lv_len AND iv_src+lv_i(3) = `...`.
CLEAR ls_tok.
ls_tok-kind = 3.
ls_tok-val = `...`.
APPEND ls_tok TO rt_tokens.
lv_i = lv_i + 3.
CONTINUE.
ENDIF.

" Dot-prefixed number: .5, .0e1
IF lv_ch = `.` AND lv_i + 1 < lv_len.
Expand Down Expand Up @@ -1512,6 +1520,38 @@ CLASS zcl_mjs IMPLEMENTATION.
ENDIF.
ENDLOOP.

ELSEIF ls_iter-type = 6 AND ls_iter-obj IS BOUND AND ls_iter-obj->has( `[[SetData]]` ).
" Mock for Map/Set iteration
DATA(lr_iter_data_ref) = ls_iter-obj->get( `[[SetData]]` ).
IF lr_iter_data_ref IS BOUND.
DATA(ls_iter_data_val) = unbox_value( lr_iter_data_ref ).
IF ls_iter_data_val-type = 7 AND ls_iter_data_val-arr IS BOUND.
LOOP AT ls_iter_data_val-arr->items INTO DATA(lr_mi_item).
DATA(ls_mi_item_val) = unbox_value( lr_mi_item ).
CREATE OBJECT lo_iter_env EXPORTING io_parent = io_env.
lo_iter_env->output = io_env->output.
IF lv_of_decl = abap_true.
lo_iter_env->set( iv_name = lv_of_name is_val = ls_mi_item_val ).
ELSE.
io_env->set( iv_name = lv_of_name is_val = ls_mi_item_val ).
ENDIF.
LOOP AT <n>-body INTO DATA(lr_mi_ofb).
eval_node( ir_node = lr_mi_ofb io_env = lo_iter_env ).
IF lo_iter_env->returning = abap_true OR lo_iter_env->breaking = abap_true OR lo_iter_env->continuing = abap_true.
EXIT.
ENDIF.
ENDLOOP.
IF lo_iter_env->continuing = abap_true.
lo_iter_env->continuing = abap_false.
ENDIF.
IF lo_iter_env->returning = abap_true OR lo_iter_env->breaking = abap_true.
lv_iter_return = lo_iter_env->returning.
EXIT.
ENDIF.
ENDLOOP.
ENDIF.
ENDIF.

ELSEIF ls_iter-type = 2.
DATA(lv_slen) = strlen( ls_iter-str ).
DATA lv_si TYPE i VALUE 0.
Expand Down Expand Up @@ -2376,7 +2416,7 @@ CLASS zcl_mjs IMPLEMENTATION.
rs_val = undefined_val( ).
ENDIF.
ELSE.
rs_val = undefined_val( ).
rs_val = undefined_val( ).
ENDIF.
ENDIF.
WHEN 2.
Expand Down Expand Up @@ -2419,6 +2459,27 @@ CLASS zcl_mjs IMPLEMENTATION.
ENDMETHOD.

METHOD eval_method_call.
DATA ls_map_cb TYPE zif_mjs=>ty_value.
DATA lo_map_arr TYPE REF TO zcl_mjs_arr.
DATA lv_map_idx TYPE i.
DATA lr_map_item TYPE REF TO data.
DATA ls_map_elem TYPE zif_mjs=>ty_value.
DATA lt_map_args TYPE zif_mjs=>tt_value_slots.
DATA ls_map_result TYPE zif_mjs=>ty_value.
DATA ls_find_cb TYPE zif_mjs=>ty_value.
DATA lv_find_idx TYPE i.
DATA lr_find_item TYPE REF TO data.
DATA ls_find_elem TYPE zif_mjs=>ty_value.
DATA lt_find_args TYPE zif_mjs=>tt_value_slots.
DATA ls_find_result TYPE zif_mjs=>ty_value.
DATA ls_flt_cb TYPE zif_mjs=>ty_value.
DATA lo_flt_arr TYPE REF TO zcl_mjs_arr.
DATA lv_flt_idx TYPE i.
DATA lr_flt_item TYPE REF TO data.
DATA ls_flt_elem TYPE zif_mjs=>ty_value.
DATA lt_flt_args TYPE zif_mjs=>tt_value_slots.
DATA ls_flt_result TYPE zif_mjs=>ty_value.

" check if method is defined on the object it was called on, or its prototype
DATA(ls_mval) = eval_property_access( is_obj = is_obj iv_prop = iv_method io_env = io_env ).

Expand All @@ -2440,6 +2501,90 @@ CLASS zcl_mjs IMPLEMENTATION.
RETURN.
ENDIF.

" Handle built-in array methods (map, filter, find) when array is returned from function or otherwise not a direct variable
IF is_obj-type = 7.
IF iv_method = `map`.
IF lines( it_args ) > 0.
READ TABLE it_args INDEX 1 INTO ls_map_cb.
IF ls_map_cb-type = 4 AND ls_map_cb-fn IS BOUND.
CREATE OBJECT lo_map_arr.
lv_map_idx = 0.
LOOP AT is_obj-arr->items INTO lr_map_item.
ls_map_elem = unbox_value( lr_map_item ).
CLEAR lt_map_args.
APPEND ls_map_elem TO lt_map_args.
APPEND number_val( CONV f( lv_map_idx ) ) TO lt_map_args.
APPEND is_obj TO lt_map_args.
ls_map_result = call_function(
ir_fn = ls_map_cb-fn
it_args = lt_map_args
io_env = io_env ).
lo_map_arr->push( box_value( ls_map_result ) ).
lv_map_idx = lv_map_idx + 1.
ENDLOOP.
rs_val-type = 7.
rs_val-arr = lo_map_arr.
ENDIF.
ELSE.
rs_val = array_val( VALUE #( ) ).
ENDIF.
RETURN.
ELSEIF iv_method = `filter`.
IF lines( it_args ) > 0.
READ TABLE it_args INDEX 1 INTO ls_flt_cb.
IF ls_flt_cb-type = 4 AND ls_flt_cb-fn IS BOUND.
CREATE OBJECT lo_flt_arr.
lv_flt_idx = 0.
LOOP AT is_obj-arr->items INTO lr_flt_item.
ls_flt_elem = unbox_value( lr_flt_item ).
CLEAR lt_flt_args.
APPEND ls_flt_elem TO lt_flt_args.
APPEND number_val( CONV f( lv_flt_idx ) ) TO lt_flt_args.
APPEND is_obj TO lt_flt_args.
ls_flt_result = call_function(
ir_fn = ls_flt_cb-fn
it_args = lt_flt_args
io_env = io_env ).
IF is_true( ls_flt_result ) = abap_true.
lo_flt_arr->push( lr_flt_item ).
ENDIF.
lv_flt_idx = lv_flt_idx + 1.
ENDLOOP.
rs_val-type = 7.
rs_val-arr = lo_flt_arr.
ENDIF.
ELSE.
rs_val = array_val( VALUE #( ) ).
ENDIF.
RETURN.
ELSEIF iv_method = `find`.
rs_val = undefined_val( ).
IF lines( it_args ) > 0.
READ TABLE it_args INDEX 1 INTO ls_find_cb.
IF ls_find_cb-type = 4 AND ls_find_cb-fn IS BOUND.
lv_find_idx = 0.
LOOP AT is_obj-arr->items INTO lr_find_item.
ls_find_elem = unbox_value( lr_find_item ).
CLEAR lt_find_args.
APPEND ls_find_elem TO lt_find_args.
APPEND number_val( CONV f( lv_find_idx ) ) TO lt_find_args.
APPEND is_obj TO lt_find_args.
ls_find_result = call_function(
ir_fn = ls_find_cb-fn
it_args = lt_find_args
io_env = io_env ).
IF is_true( ls_find_result ) = abap_true.
rs_val = ls_find_elem.
RETURN.
ENDIF.
lv_find_idx = lv_find_idx + 1.
ENDLOOP.
ENDIF.
ENDIF.
RETURN.
ENDIF.
ENDIF.

CASE is_obj-type.
WHEN 6.
IF iv_method = `has` AND is_obj-obj->has( `[[SetData]]` ).
Expand Down Expand Up @@ -2488,21 +2633,17 @@ CLASS zcl_mjs IMPLEMENTATION.
RETURN.
WHEN `map`.
IF lines( it_args ) > 0.
DATA ls_map_cb TYPE zif_mjs=>ty_value.
READ TABLE it_args INDEX 1 INTO ls_map_cb.
IF ls_map_cb-type = 4 AND ls_map_cb-fn IS BOUND.
DATA lo_map_arr TYPE REF TO zcl_mjs_arr.
CREATE OBJECT lo_map_arr.
DATA lv_map_idx TYPE i VALUE 0.
LOOP AT is_obj-arr->items INTO DATA(lr_map_item).
DATA ls_map_elem TYPE zif_mjs=>ty_value.
lv_map_idx = 0.
LOOP AT is_obj-arr->items INTO lr_map_item.
ls_map_elem = unbox_value( lr_map_item ).
DATA lt_map_args TYPE zif_mjs=>tt_value_slots.
CLEAR lt_map_args.
APPEND ls_map_elem TO lt_map_args.
APPEND number_val( CONV f( lv_map_idx ) ) TO lt_map_args.
APPEND is_obj TO lt_map_args.
DATA(ls_map_result) = call_function(
ls_map_result = call_function(
ir_fn = ls_map_cb-fn
it_args = lt_map_args
io_env = io_env ).
Expand All @@ -2518,19 +2659,16 @@ CLASS zcl_mjs IMPLEMENTATION.
WHEN `find`.
rs_val = undefined_val( ).
IF lines( it_args ) > 0.
DATA ls_find_cb TYPE zif_mjs=>ty_value.
READ TABLE it_args INDEX 1 INTO ls_find_cb.
IF ls_find_cb-type = 4 AND ls_find_cb-fn IS BOUND.
DATA lv_find_idx TYPE i VALUE 0.
LOOP AT is_obj-arr->items INTO DATA(lr_find_item).
DATA ls_find_elem TYPE zif_mjs=>ty_value.
lv_find_idx = 0.
LOOP AT is_obj-arr->items INTO lr_find_item.
ls_find_elem = unbox_value( lr_find_item ).
DATA lt_find_args TYPE zif_mjs=>tt_value_slots.
CLEAR lt_find_args.
APPEND ls_find_elem TO lt_find_args.
APPEND number_val( CONV f( lv_find_idx ) ) TO lt_find_args.
APPEND is_obj TO lt_find_args.
DATA(ls_find_result) = call_function(
ls_find_result = call_function(
ir_fn = ls_find_cb-fn
it_args = lt_find_args
io_env = io_env ).
Expand All @@ -2545,21 +2683,17 @@ CLASS zcl_mjs IMPLEMENTATION.
RETURN.
WHEN `filter`.
IF lines( it_args ) > 0.
DATA ls_flt_cb TYPE zif_mjs=>ty_value.
READ TABLE it_args INDEX 1 INTO ls_flt_cb.
IF ls_flt_cb-type = 4 AND ls_flt_cb-fn IS BOUND.
DATA lo_flt_arr TYPE REF TO zcl_mjs_arr.
CREATE OBJECT lo_flt_arr.
DATA lv_flt_idx TYPE i VALUE 0.
LOOP AT is_obj-arr->items INTO DATA(lr_flt_item).
DATA ls_flt_elem TYPE zif_mjs=>ty_value.
lv_flt_idx = 0.
LOOP AT is_obj-arr->items INTO lr_flt_item.
ls_flt_elem = unbox_value( lr_flt_item ).
DATA lt_flt_args TYPE zif_mjs=>tt_value_slots.
CLEAR lt_flt_args.
APPEND ls_flt_elem TO lt_flt_args.
APPEND number_val( CONV f( lv_flt_idx ) ) TO lt_flt_args.
APPEND is_obj TO lt_flt_args.
DATA(ls_flt_result) = call_function(
ls_flt_result = call_function(
ir_fn = ls_flt_cb-fn
it_args = lt_flt_args
io_env = io_env ).
Expand Down
26 changes: 26 additions & 0 deletions src/zcl_mjs.clas.testclasses.abap
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ CLASS ltcl_test DEFINITION FOR TESTING
METHODS test_class_typeof FOR TESTING RAISING zcx_mjs_runtime.
METHODS test_array_filter FOR TESTING RAISING zcx_mjs_runtime.
METHODS test_date_now FOR TESTING RAISING zcx_mjs_runtime.
METHODS test_array_map FOR TESTING RAISING zcx_mjs_runtime.
METHODS test_rest_map FOR TESTING RAISING zcx_mjs_runtime.

METHODS test262 FOR TESTING RAISING zcx_mjs_runtime.

Expand Down Expand Up @@ -876,6 +878,30 @@ CLASS ltcl_test IMPLEMENTATION.
exp = |true| ).
ENDMETHOD.

METHOD test_array_map.
DATA(lv_nl) = cl_abap_char_utilities=>newline.
DATA(lv_js) =
`const arr = [1, 2, 3];` && lv_nl &&
`const mapped = arr.map(x => x * 2);` && lv_nl &&
`console.log(mapped[0] + " " + mapped.length);`.
cl_abap_unit_assert=>assert_equals(
act = trim( zcl_mjs=>eval( lv_js ) )
exp = |2 3| ).
ENDMETHOD.

METHOD test_rest_map.
DATA(lv_nl) = cl_abap_char_utilities=>newline.
DATA(lv_js) =
`function double(...rest) {` && lv_nl &&
` return rest.map(x => x * 2);` && lv_nl &&
`}` && lv_nl &&
`const res = double(1, 2, 3);` && lv_nl &&
`console.log(res[0] + " " + res.length);`.
cl_abap_unit_assert=>assert_equals(
act = trim( zcl_mjs=>eval( lv_js ) )
exp = |2 3| ).
ENDMETHOD.

METHOD test262.
DATA(lv_nl) = cl_abap_char_utilities=>newline.
DATA(lv_js) =
Expand Down
32 changes: 6 additions & 26 deletions src/zcl_mjs_parser.clas.abap
Original file line number Diff line number Diff line change
Expand Up @@ -1245,21 +1245,11 @@ CLASS zcl_mjs_parser IMPLEMENTATION.
expect( `[` ).
DATA lt_elems TYPE STANDARD TABLE OF REF TO data WITH DEFAULT KEY.
WHILE peek( )-val <> `]` AND peek( )-kind <> 5.
DATA(lv_is_spread) = abap_false.
IF peek( )-val = `.`.
DATA(lv_saved) = pos.
next( ).
IF peek( )-val = `.`.
DATA(lv_is_spread) = abap_false.
IF peek( )-val = `...`.
next( ).
IF peek( )-val = `.`.
next( ).
lv_is_spread = abap_true.
ENDIF.
ENDIF.
IF lv_is_spread = abap_false.
pos = lv_saved.
lv_is_spread = abap_true.
ENDIF.
ENDIF.
DATA(lr_expr) = parse_expr( ).
IF lv_is_spread = abap_true AND lr_expr IS BOUND.
FIELD-SYMBOLS <expr_node> TYPE zif_mjs=>ty_node.
Expand Down Expand Up @@ -1360,21 +1350,11 @@ CLASS zcl_mjs_parser IMPLEMENTATION.

METHOD parse_call_args.
WHILE peek( )-val <> `)` AND peek( )-kind <> 5.
DATA(lv_is_spread) = abap_false.
IF peek( )-val = `.`.
DATA(lv_saved) = pos.
next( ).
IF peek( )-val = `.`.
DATA(lv_is_spread) = abap_false.
IF peek( )-val = `...`.
next( ).
IF peek( )-val = `.`.
next( ).
lv_is_spread = abap_true.
ENDIF.
ENDIF.
IF lv_is_spread = abap_false.
pos = lv_saved.
lv_is_spread = abap_true.
ENDIF.
ENDIF.
DATA(lr_expr) = parse_expr( ).
IF lv_is_spread = abap_true AND lr_expr IS BOUND.
FIELD-SYMBOLS <expr_node> TYPE zif_mjs=>ty_node.
Expand Down
Loading